|  | // Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. 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' show Directory, File; | 
|  |  | 
|  | import 'package:test/test.dart'; | 
|  |  | 
|  | import '../shared_test_options.dart'; | 
|  | import 'test_compiler.dart'; | 
|  |  | 
|  | typedef AdditionalLibrary = ({String name, String source}); | 
|  |  | 
|  | class ExpressionCompilerTestDriver { | 
|  | final SetupCompilerOptions setup; | 
|  | late Directory _testRootDirectory; | 
|  | String source; | 
|  | late Uri input; | 
|  | late Uri output; | 
|  | late Uri packages; | 
|  | late int line; | 
|  | final List<AdditionalLibrary> additionalLibraries; | 
|  |  | 
|  | ExpressionCompilerTestDriver( | 
|  | this.setup, | 
|  | this.source, { | 
|  | this.additionalLibraries = const [], | 
|  | }) { | 
|  | line = _getEvaluationLine(source); | 
|  | var systemTempDir = Directory.systemTemp; | 
|  | _testRootDirectory = systemTempDir.createTempSync('expression_eval_test'); | 
|  | var testPackageDirectory = Directory.fromUri( | 
|  | _testRootDirectory.uri.resolve('test_package/'), | 
|  | ); | 
|  |  | 
|  | output = testPackageDirectory.uri.resolve('test.js'); | 
|  | input = testPackageDirectory.uri.resolve('test.dart'); | 
|  | File.fromUri(input) | 
|  | ..createSync(recursive: true) | 
|  | ..writeAsStringSync(source); | 
|  |  | 
|  | packages = _testRootDirectory.uri.resolve('package_config.json'); | 
|  | var packageDescriptors = [_createPackageDescriptor('test_package')]; | 
|  | for (var library in additionalLibraries) { | 
|  | var name = library.name; | 
|  | var source = library.source; | 
|  | var additionalPackageDirectory = Directory.fromUri( | 
|  | _testRootDirectory.uri.resolve('$name/'), | 
|  | ); | 
|  | var additionalFile = additionalPackageDirectory.uri.resolve('$name.dart'); | 
|  | File.fromUri(additionalFile) | 
|  | ..createSync(recursive: true) | 
|  | ..writeAsStringSync(source); | 
|  | packageDescriptors.add(_createPackageDescriptor(name)); | 
|  | } | 
|  | File.fromUri(packages) | 
|  | ..createSync() | 
|  | ..writeAsStringSync(''' | 
|  | { | 
|  | "configVersion": 2, | 
|  | "packages": [ | 
|  | ${packageDescriptors.join(',\n          ')} | 
|  | ] | 
|  | } | 
|  | '''); | 
|  | } | 
|  |  | 
|  | /// Creates a `packages_config.json` entry for a test package called [name]. | 
|  | static String _createPackageDescriptor(String name) => | 
|  | '''{ | 
|  | "name": "$name", | 
|  | "rootUri": "./$name", | 
|  | "packageUri": "./" | 
|  | }'''; | 
|  |  | 
|  | void delete() { | 
|  | _testRootDirectory.deleteSync(recursive: true); | 
|  | } | 
|  |  | 
|  | Future<TestExpressionCompiler> createCompiler() => | 
|  | TestExpressionCompiler.init( | 
|  | setup, | 
|  | input: input, | 
|  | output: output, | 
|  | packages: packages, | 
|  | ); | 
|  |  | 
|  | Future<TestCompilationResult> compile({ | 
|  | required TestExpressionCompiler compiler, | 
|  | required Map<String, String> scope, | 
|  | required String expression, | 
|  | }) async { | 
|  | return compiler.compileExpression( | 
|  | libraryUri: input, | 
|  | line: line, | 
|  | column: 1, | 
|  | scope: scope, | 
|  | expression: expression, | 
|  | ); | 
|  | } | 
|  |  | 
|  | void checkResult( | 
|  | TestCompilationResult result, { | 
|  | String? expectedError, | 
|  | dynamic expectedResult, | 
|  | }) { | 
|  | var success = expectedError == null; | 
|  | var message = success ? expectedResult! : expectedError; | 
|  |  | 
|  | expect( | 
|  | result, | 
|  | const TypeMatcher<TestCompilationResult>() | 
|  | .having((r) => r.result!, 'result', _matches(message)) | 
|  | .having((r) => r.isSuccess, 'isSuccess', success), | 
|  | ); | 
|  | } | 
|  |  | 
|  | Future<void> check({ | 
|  | TestExpressionCompiler? compiler, | 
|  | required Map<String, String> scope, | 
|  | required String expression, | 
|  | String? expectedError, | 
|  | dynamic expectedResult, | 
|  | }) async { | 
|  | compiler ??= await createCompiler(); | 
|  | var result = await compile( | 
|  | compiler: compiler, | 
|  | scope: scope, | 
|  | expression: expression, | 
|  | ); | 
|  |  | 
|  | checkResult( | 
|  | result, | 
|  | expectedError: expectedError, | 
|  | expectedResult: expectedResult, | 
|  | ); | 
|  | } | 
|  |  | 
|  | Matcher _matches(dynamic matcher) { | 
|  | if (matcher is Matcher) return matcher; | 
|  | if (matcher is! String) throw StateError('Unsupported matcher $matcher'); | 
|  |  | 
|  | var unIndented = RegExp.escape(matcher).replaceAll(RegExp('[ ]+'), '[ ]*'); | 
|  | return matches(RegExp(unIndented, multiLine: true)); | 
|  | } | 
|  |  | 
|  | static int _getEvaluationLine(String source) { | 
|  | var placeholderRegExp = RegExp(r'// Breakpoint'); | 
|  |  | 
|  | var lines = source.split('\n'); | 
|  | for (var line = 0; line < lines.length; line++) { | 
|  | var content = lines[line]; | 
|  | if (placeholderRegExp.firstMatch(content) != null) { | 
|  | return line + 1; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  | } |