Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions bricks/test_optimizer/hooks/lib/pre_gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ typedef ExitFn = Never Function(int code);

ExitFn exitFn = exit;

String skipVeryGoodOptimizationTag = 'skip_very_good_optimization';
RegExp skipVeryGoodOptimizationRegExp = RegExp(
"@Tags\\s*\\(\\s*\\[[\\s\\S]*?[\"']$skipVeryGoodOptimizationTag[\"'][\\s\\S]*?\\]\\s*\\)",
multiLine: true,
);

Future<void> run(HookContext context) async {
final packageRoot = context.vars['package-root'] as String;
final testDir = Directory(path.join(packageRoot, 'test'));
Expand All @@ -29,8 +35,13 @@ Future<void> run(HookContext context) async {

final identifierGenerator = DartIdentifierGenerator();
final testIdentifierTable = <Map<String, String>>[];
for (final entity
in testDir.listSync(recursive: true).where((entity) => entity.isTest)) {
final tests = testDir
.listSync(recursive: true)
.where((entity) => entity.isTest);

final notOptimizedTests = await getNotOptimizedTests(tests, testDir.path);

for (final entity in tests) {
final relativePath = path
.relative(entity.path, from: testDir.path)
.replaceAll(r'\', '/');
Expand All @@ -40,11 +51,52 @@ Future<void> run(HookContext context) async {
});
}

context.vars = {'tests': testIdentifierTable, 'isFlutter': isFlutter};
final optimizedTestsIdentifierTable = testIdentifierTable
.where((e) => !notOptimizedTests.contains(e['path']))
.toList();

context.vars = {
'tests': optimizedTestsIdentifierTable,
'isFlutter': isFlutter,
'notOptimizedTests': notOptimizedTests,
};
}

extension on FileSystemEntity {
bool get isTest {
return this is File && path.basename(this.path).endsWith('_test.dart');
}
}

Future<List<String>> getNotOptimizedTests(
Iterable<FileSystemEntity> tests,
String testDir,
) async {
final paths = tests.map((e) => e.path).toList();
final formattedPaths = paths.map((e) => e.replaceAll('/./', '/')).toList();

final fileFutures = formattedPaths.map(_checkFileForSkipVeryGoodOptimization);
final fileResults = await Future.wait(fileFutures);

final testWithVeryGoodTest = <String>[];
for (var i = 0; i < formattedPaths.length; i++) {
if (fileResults[i]) {
testWithVeryGoodTest.add(formattedPaths[i]);
}
}

/// Format to relative path
final relativePaths = testWithVeryGoodTest
.map((e) => path.relative(e, from: testDir))
.toList();

return relativePaths;
}

/// Check if a single file contains skip_very_good_optimization tag
Future<bool> _checkFileForSkipVeryGoodOptimization(String path) async {
final file = File(path);
if (!file.existsSync()) return false;
final content = await file.readAsString();
return skipVeryGoodOptimizationRegExp.hasMatch(content);
}
131 changes: 131 additions & 0 deletions bricks/test_optimizer/hooks/test/pre_gen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ class _FakeContext extends Fake implements HookContext {
Map<String, Object?> vars = {};
}

final notOptimizedTestContent =
'''
@Tags(['${pre_gen.skipVeryGoodOptimizationTag}'])
void main() {
test('test', () {
expect(1, 1);
});
}
''';

final anotherNotOptimizedTestContent =
'''
@Tags(['${pre_gen.skipVeryGoodOptimizationTag}', 'another_tag'])
void main() {
test('another test', () {
expect(1, 1);
});
}
''';

void main() {
late Directory tempDirectory;

Expand Down Expand Up @@ -86,6 +106,57 @@ dependencies:

expect(context.vars['isFlutter'], true);
});

test('with proper not optimized tests identification', () async {
File(path.join(tempDirectory.path, 'pubspec.yaml')).createSync();

final testDir = Directory(path.join(tempDirectory.path, 'test'))
..createSync();
File(path.join(testDir.path, 'test1_test.dart')).createSync();
File(path.join(testDir.path, 'test2_test.dart')).createSync();
File(path.join(testDir.path, 'no_test_here.dart')).createSync();
File(
path.join(testDir.path, 'not_optimized_test.dart'),
).writeAsStringSync(notOptimizedTestContent);
File(
path.join(testDir.path, 'another_not_optimized_test.dart'),
).writeAsStringSync(anotherNotOptimizedTestContent);

context.vars['package-root'] = tempDirectory.absolute.path;

await pre_gen.run(context);

final tests = context.vars['tests'] as List<Map<String, String>>;
final testsMap = <String, String>{};
for (final test in tests) {
final path = test['path']!;
final identifier = test['identifier']!;
testsMap[path] = identifier;
}

final paths = testsMap.keys;
expect(paths, contains('test1_test.dart'));
expect(paths, contains('test2_test.dart'));
expect(paths, isNot(contains('no_test_here.dart')));
expect(paths, isNot(contains('not_optimized_test.dart')));
expect(paths, isNot(contains('another_not_optimized_test.dart')));

expect(
testsMap.values.toSet().length,
equals(tests.length),
reason: 'All tests files should have unique identifiers',
);
final notOptimizedTests =
context.vars['notOptimizedTests'] as List<String>;
expect(
notOptimizedTests,
contains('not_optimized_test.dart'),
);
expect(
notOptimizedTests,
contains('another_not_optimized_test.dart'),
);
});
});

group('Fails', () {
Expand Down Expand Up @@ -155,5 +226,65 @@ dependencies:
expect(context.vars['isFlutter'], isNull);
});
});

group('skipVeryGoodOptimizationRegExp regex', () {
final regex = pre_gen.skipVeryGoodOptimizationRegExp;
test('matches single-line tag', () {
final content = "@Tags(['${pre_gen.skipVeryGoodOptimizationTag}'])";
expect(regex.hasMatch(content), isTrue);
});

test('matches single-line with multiple tags', () {
final content =
"@Tags(['${pre_gen.skipVeryGoodOptimizationTag}', 'chrome'])";
expect(regex.hasMatch(content), isTrue);
});

test('matches multi-line tag list', () {
final content =
'''
@Tags([
'${pre_gen.skipVeryGoodOptimizationTag}',
'chrome',
'test',
])
''';
expect(regex.hasMatch(content), isTrue);
});

test('matches multi-line where tag is not the first', () {
final content =
'''
@Tags([
'chrome',
'${pre_gen.skipVeryGoodOptimizationTag}',
'test',
])
''';
expect(regex.hasMatch(content), isTrue);
});

test('does not match when tag missing', () {
const content = "@Tags(['chrome', 'test'])";
expect(regex.hasMatch(content), isFalse);
});

test(
'does not match substring only (e.g. skip_very_good_optimization,test)',
() {
final content =
'''
@Tags([
'${pre_gen.skipVeryGoodOptimizationTag},test',
'chrome',
])
''';
expect(
regex.hasMatch(content),
isFalse,
); // only exact tag should match
},
);
});
});
}
Loading