Files
sdk/pkg/sourcemap_testing
Lasse R.H. Nielsen b464fc9037 Reland "Tweak expect.dart library."
This reverts commit cd2c566bcf.

Reason for revert: Updating to not remove field used by Flutter engine.

Original change's description:
> Revert "Tweak `expect.dart` library."
>
> This reverts commit ff5f391c0a.
>
> Reason for revert: The expect library is used by Flutter engine, and some of its tests use assertStatementsEnabled. There should be a migration path that doesn't require an atomic change, like adding the replacement api before removing the old one.
>
> Original change's description:
> > Tweak `expect.dart` library.
> >
> > Make API more consistent for a few methods.
> > Reduce the number of language features used in tests:
> > * Never iterating an iterable, always converting it
> >   using `.toList()` first and iterating using indices
> >   (fx `setEquals`).
> >   Also require a `List` in places where an `Iterable`
> >   wasn't necessary.
> > * Avoid doing complicated computations that are also
> >   used for the error message. Do simple check first,
> >   then recompute to get better error messages
> >   (fx `allDistinct`).
> >
> > Renamed some rarely used members for consistency
> > (`stringContainsInOrder`->`containsInOrder`,
> > where other string-contains functions just start
> > with `contains`, and `containsOneOf` -> `containsAny`
> > to match `Iterable.any` phrasing, and also it accepts
> > if containing at least one, not precisely one.)
> >
> > Removed a function that wasn't used anywhere.
> >
> > Moved `assertStatementsEnabled` to `variations.dart` as `asserts`.
> > Removed `typeAssertionsEnabled` and `checkedModeEnabled`. The former used in one place, where it was replaced with `checkedImplicitDowncasts` from `variations.dart`, the latter wasn't used anywhere.
> >
> > Deprecates `package:expect/minitest.dart`. It was never intended
> > to be used for new tests, only as a help to convert existing tests
> > written against `package:unit_test`.
> > All existing imports marked as `// ignore: deprecated_member_use`.
> >
> > Change-Id: I07e21d4c0f3ccf11b82ee34af2668fdbb22264d2
> > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352360
> > Reviewed-by: Slava Egorov <vegorov@google.com>
> > Reviewed-by: Ömer Ağacan <omersa@google.com>
> > Reviewed-by: Nate Bosch <nbosch@google.com>
> > Reviewed-by: Stephen Adams <sra@google.com>
> > Commit-Queue: Lasse Nielsen <lrn@google.com>
>
> Change-Id: I360b4347470a0bb2b63c3108e2b83ee2a771bf3f
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362020
> Reviewed-by: Nate Bosch <nbosch@google.com>
> Reviewed-by: Ömer Ağacan <omersa@google.com>
> Reviewed-by: Stephen Adams <sra@google.com>
> Reviewed-by: Leaf Petersen <leafp@google.com>
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> Commit-Queue: William Hesse <whesse@google.com>

CoreLibraryReviewExempt: Reland
Change-Id: I53db40edc0733842a008839c3913d51c885e39ab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/362502
Reviewed-by: Alexander Thomas <athom@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: William Hesse <whesse@google.com>
Commit-Queue: Lasse Nielsen <lrn@google.com>
2024-04-26 15:28:26 +00:00
..
2022-02-14 14:06:34 +00:00

Testing of source maps

Currently this package consists of two "frameworks":

  1. stacktrace_helper.dart
  2. stepping_helper.dart

It is intended to be a shared resource between DDC and dart2js.

Stacktraces ("stacktrace_helper.dart")

TODO

Debugging tests (step tests, "stepping_helper.dart")

This is supposed to work in a few steps:

  1. Create the JS
  2. Run the JS with D8, setting instructed breakpoints etc.
  3. Translating JS positions to Dart positions
  4. Validating the stepped positions.

In the above, the helper assumes

  1. The dart file is called "test.dart" and is placed in the output directory before compiling to "js.js" in the output dir.

And then performs via 2) runD8AndStep 3) checkD8Steps 4) (done in above step)

The test files themselves contain information about where to stop, which breakpoints to expect etc. They contain this information in comments inlined in the code as in /*key*/ where key can be one of the below.

Not context sensitive

These comments can be anywhere in the file and their position does not matter.

  • fail: Will fail the test. Useful for debugging in conjunction with debug being set to true (see below).
  • Debugger:stepOver: Will step over breakpoints. Default (i.e. without this) is to step into.

Context sensitive

These comments should be placed at the wanted position: The line and possibly column position of the comment matters. They refer to the next non-whitespace position in the source.

  • bl (break line): insert a breakpoint on this line. This does not add any new expected breaks.
  • s:{i} (stop): adds an expected stop as the ith stop (1-indexed).
  • sl:{i} (stop at line): adds an expected stop as the ith stop (1-indexed). Only check the line number.
  • nb (no break): The debugger should never break on this line.
  • nbc (no break column): The debugger should never break on this line and column.
  • nbb:{i}:{j} (no break between): The debugger should not break on this line between expectation i and j (1-indexed). Note that from can also be the special value 0 meaning from the very first stop. For example nbb:0:1 means not before first expected stop.
  • nm (no mapping): There's not allowed to be any mapping to this line.
  • bc:{i} (break column): inserts a breakpoint at this line and column and adds an expected stop as the ith stop (1-indexed).

Note that in an ideal world bc:{i} would not be unnecessary: Stopping at a line and stepping should generally be enough. Because of the current behavior of d8 though, for instance

baz(foo(), bar())

will stop at baz, go into foo, stop at bar, go into bar and stop at baz. From a Dart perspective we would instead expect it to stop at foo, go into foo, stop at bar, go into bar and stop a baz. Having bc:{i} allows us to force this behavior as d8 can actually stop at foo too.

All of these annotations are removed before compiling to js and the expected output thus refers to the unannotated code.

When the test confirms that the debugger stopped at the expected locations it allows for additional breakpoints before, between and after the expected breakpoints.

Debugging a test

By calling checkD8Steps with the debug parameter set to true one can get information like this dumped to standard out:

Stop #1

test.main = function() {                            |  main() {
  try {                                             |    try {
    let value = /*STOP*/"world";                    |      var value = /*STOP*/"world";
    dart.throw(dart.str`Hello, ${value}`);          |      // Comment
  } catch (e) {                                     |      throw "Hello, $value";

Stop #2

  try {                                             |      var value = "world";
    let value = "world";                            |      // Comment
    /*STOP*/dart.throw(dart.str`Hello, ${value}`);  |      /*STOP*/throw "Hello, $value";
  } catch (e) {                                     |    }
    let st = dart.stackTrace(e);                    |    // Comment

Stop #3

    dart.throw(dart.str`Hello, ${value}`);          |    }
  } catch (e) {                                     |    // Comment
    let st = /*STOP*/dart.stackTrace(e);            |    catch (e, /*STOP*/st) {
    {                                               |      print(e);
      core.print(e);                                |      print(st);

[...]

This can for instance be useful in combination with /*fail*/ when adding new tests to see all the places where the debugger stopped.

Technical details

Some of the logic comes from https://github.com/ChromeDevTools/devtools-frontend/, for instance see https://github.com/ChromeDevTools/devtools-frontend/blob/fa18d70a995f06cb73365b2e5b8ae974cf60bd3a/front_end/sources/JavaScriptSourceFrame.js#L1520-L1523 for how a line breakpoint is resolved: Basically the line asked to break on in user code (e.g. in dart code) is asked for first and last javascript positions; these are then used to get possible breakpoints in that part. If there are none it tries the next line (etc for a number of lines). Once it finds something (in javascript positions) it converts that to user code position (e.g. in dart code), normalizes it by converting to javascript position and back to user code position again, then converts to javascript position and sets the breakpoint. This is to some extend mimicked here when setting a line break (though not a "column break").