| // 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 'package:expect/expect.dart'; |
| import 'package:js_ast/js_ast.dart'; |
| |
| class Source implements JavaScriptNodeSourceInformation { |
| final Object tag; |
| Source(this.tag); |
| |
| @override |
| String toString() => 'Source($tag)'; |
| } |
| |
| void check(Node node, expectedSource, expectedAnnotations) { |
| Expect.equals('$expectedSource', '${node.sourceInformation}', 'source'); |
| Expect.equals('$expectedAnnotations', '${node.annotations}', 'annotations'); |
| } |
| |
| void simpleTests(Node node) { |
| check(node, null, []); |
| |
| final s1 = node.withSourceInformation(Source(1)); |
| check(node, null, []); |
| check(s1, Source(1), []); |
| |
| final a1 = node.withAnnotation(1); |
| check(node, null, []); |
| check(s1, Source(1), []); |
| check(a1, null, [1]); |
| |
| final a2 = node.withAnnotation(2); |
| check(node, null, []); |
| check(s1, Source(1), []); |
| check(a1, null, [1]); |
| check(a2, null, [2]); |
| |
| final s1a3 = s1.withAnnotation(3); |
| check(node, null, []); |
| check(s1, Source(1), []); |
| check(s1, Source(1), []); |
| check(a2, null, [2]); |
| check(s1a3, Source(1), [3]); |
| |
| final s1a3a4 = s1a3.withAnnotation(4); |
| check(node, null, []); |
| check(s1, Source(1), []); |
| check(s1, Source(1), []); |
| check(a2, null, [2]); |
| check(s1a3, Source(1), [3]); |
| check(s1a3a4, Source(1), [3, 4]); |
| |
| final a2s5 = a2.withSourceInformation(Source(5)); |
| check(node, null, []); |
| check(s1, Source(1), []); |
| check(s1, Source(1), []); |
| check(a2, null, [2]); |
| check(s1a3, Source(1), [3]); |
| check(s1a3a4, Source(1), [3, 4]); |
| check(a2s5, Source(5), [2]); |
| } |
| |
| bool debugging = false; |
| |
| /// Explore all combinations of withSourceInformation and withAnnotation. |
| void testGraph(Node root) { |
| // At each node in the graph, all the previous checks are re-run to ensure |
| // that source information or annotations do not change. |
| List<void Function(String state)> tests = []; |
| |
| void explore( |
| String state, |
| Node node, |
| int sourceDepth, |
| int annotationDepth, |
| JavaScriptNodeSourceInformation? expectedSource, |
| List<Object> expectedAnnotations) { |
| void newCheck(String currentState) { |
| if (debugging) { |
| print('In state $currentState check $state:' |
| ' source: $expectedSource, annotations: $expectedAnnotations'); |
| } |
| Expect.equals( |
| '$expectedSource', |
| '${node.sourceInformation}', |
| ' at state $currentState for node at state $state:' |
| ' ${node.debugPrint()}'); |
| Expect.equals( |
| '$expectedAnnotations', |
| '${node.annotations}', |
| ' at state $currentState for node at state $state:' |
| ' ${node.debugPrint()}'); |
| } |
| |
| tests.add(newCheck); |
| |
| for (final test in tests) { |
| test(state); |
| } |
| |
| if (sourceDepth < 3) { |
| final newSourceDepth = sourceDepth + 1; |
| final newSource = Source(newSourceDepth); |
| final newState = '$state-s$newSourceDepth'; |
| final newNode = node.withSourceInformation(newSource); |
| |
| explore(newState, newNode, newSourceDepth, annotationDepth, newSource, |
| expectedAnnotations); |
| } |
| if (annotationDepth < 3) { |
| final newAnnotationDepth = annotationDepth + 1; |
| final newAnnotation = 'a:$newAnnotationDepth'; |
| final newAnnotations = [...expectedAnnotations, newAnnotation]; |
| final newState = '$state-a$newAnnotationDepth'; |
| final newNode = node.withAnnotation(newAnnotation); |
| |
| explore(newState, newNode, sourceDepth, newAnnotationDepth, |
| expectedSource, newAnnotations); |
| } |
| } |
| |
| explore('root', root, 0, 0, null, []); |
| } |
| |
| void main() { |
| simpleTests(js('x + 1')); |
| simpleTests(js.statement('f()')); |
| |
| testGraph(js('1')); |
| testGraph(js('x + 1')); |
| testGraph(js('f()')); |
| testGraph(js.statement('f()')); |
| testGraph(js.statement('break')); |
| } |