content: // Copyright 2018 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  @TestOn('vm')
  library;
  
  import 'package:test/test.dart';
  import 'package:source_span/source_span.dart';
  import 'package:stack_trace/stack_trace.dart';
  
  import 'package:sass/sass.dart';
  
  import 'test_importer.dart';
  
  void main() {
    group("with @warn", () {
      test("passes the message and stack trace to the logger", () {
        var mustBeCalled = expectAsync0(() {});
        compileString(
          '''
            @mixin foo {@warn heck}
            @include foo;
          ''',
          logger: _TestLogger.withWarn((
            message, {
            span,
            trace,
            deprecation = false,
          }) {
            expect(message, equals("heck"));
            expect(span, isNull);
            expect(trace!.frames.first.member, equals('foo()'));
            expect(deprecation, isFalse);
            mustBeCalled();
          }),
        );
      });
  
      test("stringifies the argument", () {
        var mustBeCalled = expectAsync0(() {});
        compileString(
          '@warn #abc',
          logger: _TestLogger.withWarn((
            message, {
            span,
            trace,
            deprecation = false,
          }) {
            expect(message, equals("#abc"));
            mustBeCalled();
          }),
        );
      });
  
      test("doesn't inspect the argument", () {
        var mustBeCalled = expectAsync0(() {});
        compileString(
          '@warn null',
          logger: _TestLogger.withWarn((
            message, {
            span,
            trace,
            deprecation = false,
          }) {
            expect(message, isEmpty);
            mustBeCalled();
          }),
        );
      });
    });
  
    group("with @debug", () {
      test("passes the message and span to the logger", () {
        compileString(
          '@debug heck',
          logger: _TestLogger.withDebug(
            expectAsync2((message, span) {
              expect(message, equals("heck"));
              expect(span.start.line, equals(0));
              expect(span.start.column, equals(0));
              expect(span.end.line, equals(0));
              expect(span.end.column, equals(11));
            }),
          ),
        );
      });
  
      test("stringifies the argument", () {
        compileString(
          '@debug #abc',
          logger: _TestLogger.withDebug(
            expectAsync2((message, span) {
              expect(message, equals("#abc"));
            }),
          ),
        );
      });
  
      test("inspects the argument", () {
        compileString(
          '@debug null',
          logger: _TestLogger.withDebug(
            expectAsync2((message, span) {
              expect(message, equals("null"));
            }),
          ),
        );
      });
    });
  
    test("with a parser warning passes the message, span, and trace", () {
      var mustBeCalled = expectAsync0(() {});
      compileString(
        'a {b: c && d}',
        logger: _TestLogger.withWarn((
          message, {
          span,
          trace,
          deprecation = false,
        }) {
          expect(message, contains('"&&" means two copies'));
  
          expect(span!.start.line, equals(0));
          expect(span.start.column, equals(8));
          expect(span.end.line, equals(0));
          expect(span.end.column, equals(10));
  
          expect(trace!.frames.first.member, equals('root stylesheet'));
          expect(deprecation, isFalse);
          mustBeCalled();
        }),
      );
    });
  
    test("with a runner warning passes the message, span, and trace", () {
      var mustBeCalled = expectAsync0(() {});
      compileString(
        '''
          @mixin foo {#{blue} {x: y}}
          @include foo;
        ''',
        logger: _TestLogger.withWarn((
          message, {
          span,
          trace,
          deprecation = false,
        }) {
          expect(message, contains("color value blue"));
  
          expect(span!.start.line, equals(0));
          expect(span.start.column, equals(22));
          expect(span.end.line, equals(0));
          expect(span.end.column, equals(26));
  
          expect(trace!.frames.first.member, equals('foo()'));
          expect(deprecation, isFalse);
          mustBeCalled();
        }),
      );
    });
  
    group("with warn()", () {
      group("from a function", () {
        test("synchronously", () {
          var mustBeCalled = expectAsync0(() {});
          compileString(
            """
              @function bar() {@return foo()}
              a {b: bar()}
            """,
            functions: [
              Callable(
                "foo",
                "",
                expectAsync1((_) {
                  warn("heck");
                  return sassNull;
                }),
              ),
            ],
            logger: _TestLogger.withWarn((
              message, {
              span,
              trace,
              deprecation = false,
            }) {
              expect(message, equals("heck"));
  
              expect(span!.start.line, equals(0));
              expect(span.start.column, equals(37));
              expect(span.end.line, equals(0));
              expect(span.end.column, equals(42));
  
              expect(trace!.frames.first.member, equals('bar()'));
              expect(deprecation, isFalse);
              mustBeCalled();
            }),
          );
        });
  
        test("asynchronously", () {
          var mustBeCalled = expectAsync0(() {});
          compileStringAsync(
            """
              @function bar() {@return foo()}
              a {b: bar()}
            """,
            functions: [
              AsyncCallable(
                "foo",
                "",
                expectAsync1((_) async {
                  warn("heck");
                  return sassNull;
                }),
              ),
            ],
            logger: _TestLogger.withWarn((
              message, {
              span,
              trace,
              deprecation = false,
            }) {
              expect(message, equals("heck"));
  
              expect(span!.start.line, equals(0));
              expect(span.start.column, equals(37));
              expect(span.end.line, equals(0));
              expect(span.end.column, equals(42));
  
              expect(trace!.frames.first.member, equals('bar()'));
              expect(deprecation, isFalse);
              mustBeCalled();
            }),
          );
        });
  
        test("asynchronously after a gap", () {
          var mustBeCalled = expectAsync0(() {});
          compileStringAsync(
            """
              @function bar() {@return foo()}
              a {b: bar()}
            """,
            functions: [
              AsyncCallable(
                "foo",
                "",
                expectAsync1((_) async {
                  await Future<void>.delayed(Duration.zero);
                  warn("heck");
                  return sassNull;
                }),
              ),
            ],
            logger: _TestLogger.withWarn((
              message, {
              span,
              trace,
              deprecation = false,
            }) {
              expect(message, equals("heck"));
  
              expect(span!.start.line, equals(0));
              expect(span.start.column, equals(37));
              expect(span.end.line, equals(0));
              expect(span.end.column, equals(42));
  
              expect(trace!.frames.first.member, equals('bar()'));
              expect(deprecation, isFalse);
              mustBeCalled();
            }),
          );
        });
      });
  
      test("from an importer", () {
        var mustBeCalled = expectAsync0(() {});
        compileString(
          "@use 'foo';",
          importers: [
            TestImporter((url) => Uri.parse("u:$url"), (url) {
              warn("heck");
              return ImporterResult("", indented: false);
            }),
          ],
          logger: _TestLogger.withWarn((
            message, {
            span,
            trace,
            deprecation = false,
          }) {
            expect(message, equals("heck"));
  
            expect(span!.start.line, equals(0));
            expect(span.start.column, equals(0));
            expect(span.end.line, equals(0));
            expect(span.end.column, equals(10));
  
            expect(trace!.frames.first.member, equals('@use'));
            expect(deprecation, isFalse);
            mustBeCalled();
          }),
        );
      });
  
      test("with deprecation", () {
        var mustBeCalled = expectAsync0(() {});
        compileString(
          "a {b: foo()}",
          functions: [
            Callable(
              "foo",
              "",
              expectAsync1((_) {
                warn("heck", deprecation: true);
                return sassNull;
              }),
            ),
          ],
          logger: _TestLogger.withWarn((
            message, {
            span,
            trace,
            deprecation = false,
          }) {
            expect(message, equals("heck"));
            expect(deprecation, isTrue);
            mustBeCalled();
          }),
        );
      });
    });
  }
  
  /// A [Logger] whose [warn] and [debug] methods are provided by callbacks.
  class _TestLogger implements Logger {
    final void Function(String, {FileSpan? span, Trace? trace, bool deprecation})
        _warn;
    final void Function(String, SourceSpan) _debug;
  
    _TestLogger.withWarn(this._warn) : _debug = const Logger.stderr().debug;
  
    _TestLogger.withDebug(this._debug) : _warn = const Logger.stderr().warn;
  
    void warn(
      String message, {
      FileSpan? span,
      Trace? trace,
      bool deprecation = false,
    }) =>
        _warn(message, span: span, trace: trace, deprecation: deprecation);
    void debug(String message, SourceSpan span) => _debug(message, span);
  }
  