// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:io' as io; import 'dart:typed_data'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_goldens/flutter_goldens.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; const String _kFlutterRoot = '/flutter'; const String _kRepositoryRoot = '$_kFlutterRoot/bin/cache/pkg/goldens'; const String _kVersionFile = '$_kFlutterRoot/bin/internal/goldens.version'; const String _kGoldensVersion = '123456abcdef'; void main() { MemoryFileSystem fs; FakePlatform platform; MockProcessManager process; setUp(() { fs = MemoryFileSystem(); platform = FakePlatform(environment: {'FLUTTER_ROOT': _kFlutterRoot}); process = MockProcessManager(); fs.directory(_kFlutterRoot).createSync(recursive: true); fs.directory(_kRepositoryRoot).createSync(recursive: true); fs.file(_kVersionFile).createSync(recursive: true); fs.file(_kVersionFile).writeAsStringSync(_kGoldensVersion); }); group('GoldensClient', () { GoldensRepositoryClient goldens; setUp(() { goldens = GoldensRepositoryClient( fs: fs, process: process, platform: platform, ); }); group('prepare', () { test('performs minimal work if versions match', () async { when(process.run(any, workingDirectory: anyNamed('workingDirectory'))) .thenAnswer((_) => Future.value(io.ProcessResult(123, 0, _kGoldensVersion, ''))); await goldens.prepare(); // Verify that we only spawned `git rev-parse HEAD` final VerificationResult verifyProcessRun = verify(process.run(captureAny, workingDirectory: captureAnyNamed('workingDirectory'))); verifyProcessRun.called(1); expect(verifyProcessRun.captured.first, ['git', 'rev-parse', 'HEAD']); expect(verifyProcessRun.captured.last, _kRepositoryRoot); }); }); }); group('SkiaGoldClient', () { SkiaGoldClient goldens; setUp(() { goldens = SkiaGoldClient( fs: fs, process: process, platform: platform, ); }); group('auth', () { test('performs minimal work if already authorized', () async { final Directory workDirectory = fs.directory('/workDirectory')..createSync(recursive: true); fs.file('/workDirectory/temp/auth_opt.json')..createSync(recursive: true); when(process.run(any)).thenAnswer((_) => Future.value(io.ProcessResult(123, 0, '', ''))); await goldens.auth(workDirectory); // Verify that we spawned no process calls final VerificationResult verifyProcessRun = verifyNever(process.run(captureAny, workingDirectory: captureAnyNamed('workingDirectory'))); expect(verifyProcessRun.callCount, 0); }); }); }); group('FlutterGoldenFileComparator', () { test('calculates the basedir correctly', () async { final MockSkiaGoldClient goldens = MockSkiaGoldClient(); final MockLocalFileComparator defaultComparator = MockLocalFileComparator(); final Directory flutterRoot = fs.directory('/foo')..createSync(recursive: true); final Directory goldensRoot = flutterRoot.childDirectory('bar')..createSync(recursive: true); when(goldens.fs).thenReturn(fs); when(goldens.flutterRoot).thenReturn(flutterRoot); when(goldens.comparisonRoot).thenReturn(goldensRoot); when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri); final Directory basedir = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator); expect(basedir.uri, fs.directory('/foo/bar/baz').uri); }); }); group('FlutterGoldensRepositoryFileComparator', () { MemoryFileSystem fs; FlutterGoldensRepositoryFileComparator comparator; setUp(() { fs = MemoryFileSystem(); platform = FakePlatform( operatingSystem: 'linux', environment: {'FLUTTER_ROOT': _kFlutterRoot}, ); final Directory flutterRoot = fs.directory('/path/to/flutter')..createSync(recursive: true); final Directory goldensRoot = flutterRoot.childDirectory('bin/cache/goldens')..createSync(recursive: true); final Directory testDirectory = goldensRoot.childDirectory('test/foo/bar')..createSync(recursive: true); comparator = FlutterGoldensRepositoryFileComparator( testDirectory.uri, fs: fs, platform: platform, ); }); group('compare', () { test('throws if golden file is not found', () async { try { await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); fail('TestFailure expected but not thrown'); } on TestFailure catch (error) { expect(error.message, contains('Could not be compared against non-existent file')); } }); test('returns false if golden bytes do not match', () async { final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') ..createSync(recursive: true); goldenFile.writeAsBytesSync([4, 5, 6], flush: true); final bool result = await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); expect(result, isFalse); }); test('returns true if golden bytes match', () async { final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') ..createSync(recursive: true); goldenFile.writeAsBytesSync([1, 2, 3], flush: true); final bool result = await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); expect(result, isTrue); }); }); group('update', () { test('creates golden file if it does not already exist', () async { final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png'); expect(goldenFile.existsSync(), isFalse); await comparator.update(Uri.parse('test.png'), Uint8List.fromList([1, 2, 3])); expect(goldenFile.existsSync(), isTrue); expect(goldenFile.readAsBytesSync(), [1, 2, 3]); }); test('overwrites golden bytes if golden file already exist', () async { final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') ..createSync(recursive: true); goldenFile.writeAsBytesSync([4, 5, 6], flush: true); await comparator.update(Uri.parse('test.png'), Uint8List.fromList([1, 2, 3])); expect(goldenFile.readAsBytesSync(), [1, 2, 3]); }); }); group('getTestUri', () { test('incorporates version number', () { final Uri key = comparator.getTestUri(Uri.parse('foo.png'), 1); expect(key, Uri.parse('foo.1.png')); }); test('ignores null version number', () { final Uri key = comparator.getTestUri(Uri.parse('foo.png'), null); expect(key, Uri.parse('foo.png')); }); }); }); group('FlutterSkiaGoldFileComparator', () { FlutterSkiaGoldFileComparator comparator; setUp(() { final Directory flutterRoot = fs.directory('/path/to/flutter')..createSync(recursive: true); final Directory goldensRoot = flutterRoot.childDirectory('bin/cache/goldens')..createSync(recursive: true); final Directory testDirectory = goldensRoot.childDirectory('test/foo/bar')..createSync(recursive: true); comparator = FlutterSkiaGoldFileComparator( testDirectory.uri, MockSkiaGoldClient(), fs: fs, platform: platform, ); }); group('getTestUri', () { test('ignores version number', () { final Uri key = comparator.getTestUri(Uri.parse('foo.png'), 1); expect(key, Uri.parse('foo.png')); }); }); }); } class MockProcessManager extends Mock implements ProcessManager {} class MockGoldensRepositoryClient extends Mock implements GoldensRepositoryClient {} class MockSkiaGoldClient extends Mock implements SkiaGoldClient {} class MockLocalFileComparator extends Mock implements LocalFileComparator {}