// Copyright 2014 The Flutter Authors. 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:flutter/foundation.dart' show DiagnosticLevel, FlutterError;
import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart';

import '../rendering/mock_canvas.dart';
import '../rendering/rendering_tester.dart';

class SillyBorder extends BoxBorder {
  @override
  dynamic noSuchMethod(Invocation invocation) => null;
}

void main() {
  test('BoxBorder.lerp', () {
    // names of the form fooAtX are foo, lerped X% of the way to null
    const BoxBorder directionalWithMagentaTop5 = BorderDirectional(top: BorderSide(color: Color(0xFFFF00FF), width: 5.0));
    const BoxBorder directionalWithMagentaTop5At25 = BorderDirectional(top: BorderSide(color: Color(0x3F3F003F), width: 1.25));
    const BoxBorder directionalWithMagentaTop5At75 = BorderDirectional(top: BorderSide(color: Color(0xBFBF00BF), width: 3.75));
    const BoxBorder directionalWithSides10 = BorderDirectional(start: BorderSide(width: 10.0), end: BorderSide(width: 20.0));
    const BoxBorder directionalWithSides10At25 = BorderDirectional(start: BorderSide(width: 2.5, color: Color(0x3F000000)), end: BorderSide(width: 5.0, color: Color(0x3F000000)));
    const BoxBorder directionalWithSides10At50 = BorderDirectional(start: BorderSide(width: 5.0, color: Color(0x7F000000)), end: BorderSide(width: 10.0, color: Color(0x7F000000)));
    const BoxBorder directionalWithSides10At75 = BorderDirectional(start: BorderSide(width: 7.5, color: Color(0xBF000000)), end: BorderSide(width: 15.0, color: Color(0xBF000000)));
    const BoxBorder directionalWithSides20 = BorderDirectional(start: BorderSide(width: 20.0), end: BorderSide(width: 40.0));
    const BoxBorder directionalWithSides30 = BorderDirectional(start: BorderSide(width: 30.0), end: BorderSide(width: 60.0));
    const BoxBorder directionalWithTop10 = BorderDirectional(top: BorderSide(width: 10.0));
    const BoxBorder directionalWithYellowTop10 = BorderDirectional(top: BorderSide(color: Color(0xFFFFFF00), width: 10.0));
    const BoxBorder directionalWithYellowTop5 = BorderDirectional(top: BorderSide(color: Color(0xFFFFFF00), width: 5.0));
    const BoxBorder visualWithMagentaTop10 = Border(top: BorderSide(color: Color(0xFFFF00FF), width: 10.0));
    const BoxBorder visualWithMagentaTop5 = Border(top: BorderSide(color: Color(0xFFFF00FF), width: 5.0));
    const BoxBorder visualWithSides10 = Border(left: BorderSide(width: 10.0), right: BorderSide(width: 20.0));
    const BoxBorder visualWithSides10At25 = Border(left: BorderSide(width: 2.5, color: Color(0x3F000000)), right: BorderSide(width: 5.0, color: Color(0x3F000000)));
    const BoxBorder visualWithSides10At50 = Border(left: BorderSide(width: 5.0, color: Color(0x7F000000)), right: BorderSide(width: 10.0, color: Color(0x7F000000)));
    const BoxBorder visualWithSides10At75 = Border(left: BorderSide(width: 7.5, color: Color(0xBF000000)), right: BorderSide(width: 15.0, color: Color(0xBF000000)));
    const BoxBorder visualWithSides20 = Border(left: BorderSide(width: 20.0), right: BorderSide(width: 40.0));
    const BoxBorder visualWithSides30 = Border(left: BorderSide(width: 30.0), right: BorderSide(width: 60.0));
    const BoxBorder visualWithTop10 = Border(top: BorderSide(width: 10.0));
    const BoxBorder visualWithTop100 = Border(top: BorderSide(width: 100.0));
    const BoxBorder visualWithTop190 = Border(top: BorderSide(width: 190.0));
    const BoxBorder visualWithYellowTop5 = Border(top: BorderSide(color: Color(0xFFFFFF00), width: 5.0));
    const BoxBorder visualWithYellowTop5At25 = Border(top: BorderSide(color: Color(0x3F3F3F00), width: 1.25));
    const BoxBorder visualWithYellowTop5At75 = Border(top: BorderSide(color: Color(0xBFBFBF00), width: 3.75));

    expect(BoxBorder.lerp(null, null, -1.0), null);
    expect(BoxBorder.lerp(Border.all(width: 10.0), null, -1.0), Border.all(width: 20.0));
    expect(BoxBorder.lerp(null, Border.all(width: 10.0), -1.0), Border.all(width: 0.0, style: BorderStyle.none));
    expect(BoxBorder.lerp(directionalWithTop10, null, -1.0), const BorderDirectional(top: BorderSide(width: 20.0)));
    expect(BoxBorder.lerp(null, directionalWithTop10, -1.0), const BorderDirectional());
    expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, -1.0), const Border());
    expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, -1.0), visualWithSides20);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, -1.0), const Border(top: BorderSide(color: Color(0xFFFFFF00), width: 5.0)));
    expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, -1.0), visualWithSides30);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, -1.0), directionalWithYellowTop10);
    expect(() => BoxBorder.lerp(SillyBorder(), const Border(), -1.0), throwsFlutterError);

    expect(BoxBorder.lerp(null, null, 0.0), null);
    expect(BoxBorder.lerp(Border.all(width: 10.0), null, 0.0), Border.all(width: 10.0));
    expect(BoxBorder.lerp(null, Border.all(width: 10.0), 0.0), const Border());
    expect(BoxBorder.lerp(directionalWithTop10, null, 0.0), const BorderDirectional(top: BorderSide(width: 10.0)));
    expect(BoxBorder.lerp(null, directionalWithTop10, 0.0), const BorderDirectional());
    expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 0.0), visualWithTop10);
    expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 0.0), visualWithSides10);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 0.0), const Border(top: BorderSide(color: Color(0xFFFFFF00), width: 5.0)));
    expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 0.0), visualWithSides10);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 0.0), directionalWithYellowTop5);
    expect(() => BoxBorder.lerp(SillyBorder(), const Border(), 0.0), throwsFlutterError);

    expect(BoxBorder.lerp(null, null, 0.25), null);
    expect(BoxBorder.lerp(Border.all(width: 10.0), null, 0.25), Border.all(width: 7.5));
    expect(BoxBorder.lerp(null, Border.all(width: 10.0), 0.25), Border.all(width: 2.5));
    expect(BoxBorder.lerp(directionalWithTop10, null, 0.25), const BorderDirectional(top: BorderSide(width: 7.5)));
    expect(BoxBorder.lerp(null, directionalWithTop10, 0.25), const BorderDirectional(top: BorderSide(width: 2.5)));
    expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 0.25), const Border(top: BorderSide(width: 32.5)));
    expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 0.25), visualWithSides10At75 + directionalWithMagentaTop5At25);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 0.25), Border(top: BorderSide(width: 5.0, color: Color.lerp(const Color(0xFFFFFF00), const Color(0xFFFF00FF), 0.25))));
    expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 0.25), visualWithSides10At50);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 0.25), visualWithYellowTop5At75 + directionalWithSides10At25);
    expect(() => BoxBorder.lerp(SillyBorder(), const Border(), 0.25), throwsFlutterError);

    expect(BoxBorder.lerp(null, null, 0.75), null);
    expect(BoxBorder.lerp(Border.all(width: 10.0), null, 0.75), Border.all(width: 2.5));
    expect(BoxBorder.lerp(null, Border.all(width: 10.0), 0.75), Border.all(width: 7.5));
    expect(BoxBorder.lerp(directionalWithTop10, null, 0.75), const BorderDirectional(top: BorderSide(width: 2.5)));
    expect(BoxBorder.lerp(null, directionalWithTop10, 0.75), const BorderDirectional(top: BorderSide(width: 7.5)));
    expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 0.75), const Border(top: BorderSide(width: 77.5)));
    expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 0.75), visualWithSides10At25 + directionalWithMagentaTop5At75);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 0.75), Border(top: BorderSide(width: 5.0, color: Color.lerp(const Color(0xFFFFFF00), const Color(0xFFFF00FF), 0.75))));
    expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 0.75), directionalWithSides10At50);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 0.75), visualWithYellowTop5At25 + directionalWithSides10At75);
    expect(() => BoxBorder.lerp(SillyBorder(), const Border(), 0.75), throwsFlutterError);

    expect(BoxBorder.lerp(null, null, 1.0), null);
    expect(BoxBorder.lerp(Border.all(width: 10.0), null, 1.0), Border.all(width: 0.0, style: BorderStyle.none));
    expect(BoxBorder.lerp(null, Border.all(width: 10.0), 1.0), Border.all(width: 10.0));
    expect(BoxBorder.lerp(directionalWithTop10, null, 1.0), const BorderDirectional());
    expect(BoxBorder.lerp(null, directionalWithTop10, 1.0), const BorderDirectional(top: BorderSide(width: 10.0)));
    expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 1.0), visualWithTop100);
    expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 1.0), visualWithMagentaTop5);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 1.0), visualWithMagentaTop5);
    expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 1.0), directionalWithSides10);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 1.0), directionalWithSides10);
    expect(() => BoxBorder.lerp(SillyBorder(), const Border(), 1.0), throwsFlutterError);

    expect(BoxBorder.lerp(null, null, 2.0), null);
    expect(BoxBorder.lerp(Border.all(width: 10.0), null, 2.0), Border.all(width: 0.0, style: BorderStyle.none));
    expect(BoxBorder.lerp(null, Border.all(width: 10.0), 2.0), Border.all(width: 20.0));
    expect(BoxBorder.lerp(directionalWithTop10, null, 2.0), const BorderDirectional());
    expect(BoxBorder.lerp(null, directionalWithTop10, 2.0), const BorderDirectional(top: BorderSide(width: 20.0)));
    expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 2.0), visualWithTop190);
    expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 2.0), visualWithMagentaTop10);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 2.0), visualWithMagentaTop5);
    expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 2.0), directionalWithSides30);
    expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 2.0), directionalWithSides20);
    expect(() => BoxBorder.lerp(SillyBorder(), const Border(), 2.0), throwsFlutterError);
  });

  test('BoxBorder.lerp throws correct FlutterError message', () {
    FlutterError error;
    try {
      BoxBorder.lerp(SillyBorder(), const Border(), 2.0);
    } on FlutterError catch (e) {
      error = e;
    }
    expect(error, isNotNull);
    expect(error.diagnostics.length, 3);
    expect(error.diagnostics[2].level, DiagnosticLevel.hint);
    expect(
      error.diagnostics[2].toStringDeep(),
      equalsIgnoringHashCodes(
        'For a more general interpolation method, consider using\n'
        'ShapeBorder.lerp instead.\n',
      ),
    );
    expect(error.toStringDeep(), equalsIgnoringHashCodes(
      'FlutterError\n'
      '   BoxBorder.lerp can only interpolate Border and BorderDirectional\n'
      '   classes.\n'
      '   BoxBorder.lerp() was called with two objects of type SillyBorder\n'
      '   and Border:\n'
      '     SillyBorder()\n'
      '     Border.all(BorderSide(Color(0xff000000), 0.0,\n'
      '   BorderStyle.none))\n'
      '   However, only Border and BorderDirectional classes are supported\n'
      '   by this method.\n'
      '   For a more general interpolation method, consider using\n'
      '   ShapeBorder.lerp instead.\n'
    ));
  });

  test('BoxBorder.getInnerPath / BoxBorder.getOuterPath', () {
    // for Border, BorderDirectional
    const Border border = Border(top: BorderSide(width: 10.0), right: BorderSide(width: 20.0));
    const BorderDirectional borderDirectional = BorderDirectional(top: BorderSide(width: 10.0), end: BorderSide(width: 20.0));
    expect(
      border.getOuterPath(const Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
      isPathThat(
        includes: <Offset>[
          const Offset(50.0, 60.0),
          const Offset(60.0, 60.0),
          const Offset(60.0, 70.0),
          const Offset(80.0, 190.0),
          const Offset(109.0, 189.0),
          const Offset(110.0, 80.0),
          const Offset(110.0, 190.0),
        ],
        excludes: <Offset>[
          const Offset(40.0, 60.0),
          const Offset(50.0, 50.0),
          const Offset(111.0, 190.0),
          const Offset(110.0, 191.0),
          const Offset(111.0, 191.0),
          const Offset(0.0, 0.0),
          const Offset(-10.0, -10.0),
          const Offset(0.0, -10.0),
          const Offset(-10.0, 0.0),
          const Offset(1000.0, 1000.0),
        ],
      ),
    );
    expect(
      border.getInnerPath(const Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
      // inner path is a rect from 50.0,70.0 to 90.0,190.0
      isPathThat(
        includes: <Offset>[
          const Offset(50.0, 70.0),
          const Offset(55.0, 70.0),
          const Offset(50.0, 75.0),
          const Offset(70.0, 70.0),
          const Offset(70.0, 71.0),
          const Offset(71.0, 70.0),
          const Offset(71.0, 71.0),
          const Offset(80.0, 180.0),
          const Offset(80.0, 190.0),
          const Offset(89.0, 189.0),
          const Offset(90.0, 190.0),
        ],
        excludes: <Offset>[
          const Offset(40.0, 60.0),
          const Offset(50.0, 50.0),
          const Offset(50.0, 60.0),
          const Offset(60.0, 60.0),
          const Offset(0.0, 0.0),
          const Offset(-10.0, -10.0),
          const Offset(0.0, -10.0),
          const Offset(-10.0, 0.0),
          const Offset(110.0, 80.0),
          const Offset(89.0, 191.0),
          const Offset(90.0, 191.0),
          const Offset(91.0, 189.0),
          const Offset(91.0, 190.0),
          const Offset(91.0, 191.0),
          const Offset(109.0, 189.0),
          const Offset(110.0, 190.0),
          const Offset(1000.0, 1000.0),
        ],
      ),
    );
    expect(
      borderDirectional.getOuterPath(const Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
      isPathThat(
        includes: <Offset>[
          const Offset(50.0, 60.0),
          const Offset(60.0, 60.0),
          const Offset(60.0, 70.0),
          const Offset(80.0, 190.0),
          const Offset(109.0, 189.0),
          const Offset(110.0, 80.0),
          const Offset(110.0, 190.0),
        ],
        excludes: <Offset>[
          const Offset(40.0, 60.0),
          const Offset(50.0, 50.0),
          const Offset(111.0, 190.0),
          const Offset(110.0, 191.0),
          const Offset(111.0, 191.0),
          const Offset(0.0, 0.0),
          const Offset(-10.0, -10.0),
          const Offset(0.0, -10.0),
          const Offset(-10.0, 0.0),
          const Offset(1000.0, 1000.0),
        ],
      ),
    );
    expect(
      borderDirectional.getInnerPath(const Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
      // inner path is a rect from 70.0,70.0 to 110.0,190.0
      isPathThat(
        includes: <Offset>[
          const Offset(70.0, 70.0),
          const Offset(70.0, 71.0),
          const Offset(71.0, 70.0),
          const Offset(71.0, 71.0),
          const Offset(80.0, 180.0),
          const Offset(80.0, 190.0),
          const Offset(89.0, 189.0),
          const Offset(90.0, 190.0),
          const Offset(91.0, 189.0),
          const Offset(91.0, 190.0),
          const Offset(109.0, 189.0),
          const Offset(110.0, 80.0),
          const Offset(110.0, 190.0),
        ],
        excludes: <Offset>[
          const Offset(40.0, 60.0),
          const Offset(50.0, 50.0),
          const Offset(50.0, 60.0),
          const Offset(50.0, 70.0),
          const Offset(50.0, 75.0),
          const Offset(55.0, 70.0),
          const Offset(60.0, 60.0),
          const Offset(0.0, 0.0),
          const Offset(-10.0, -10.0),
          const Offset(0.0, -10.0),
          const Offset(-10.0, 0.0),
          const Offset(89.0, 191.0),
          const Offset(90.0, 191.0),
          const Offset(91.0, 191.0),
          const Offset(110.0, 191.0),
          const Offset(111.0, 190.0),
          const Offset(111.0, 191.0),
          const Offset(1000.0, 1000.0),
        ],
      ),
    );
    expect(
      borderDirectional.getOuterPath(const Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.ltr),
      isPathThat(
        includes: <Offset>[
          const Offset(50.0, 60.0),
          const Offset(60.0, 60.0),
          const Offset(60.0, 70.0),
          const Offset(80.0, 190.0),
          const Offset(109.0, 189.0),
          const Offset(110.0, 80.0),
          const Offset(110.0, 190.0),
        ],
        excludes: <Offset>[
          const Offset(40.0, 60.0),
          const Offset(50.0, 50.0),
          const Offset(111.0, 190.0),
          const Offset(110.0, 191.0),
          const Offset(111.0, 191.0),
          const Offset(0.0, 0.0),
          const Offset(-10.0, -10.0),
          const Offset(0.0, -10.0),
          const Offset(-10.0, 0.0),
          const Offset(1000.0, 1000.0),
        ],
      ),
    );
    expect(
      borderDirectional.getInnerPath(const Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.ltr),
      // inner path is a rect from 50.0,70.0 to 90.0,190.0
      isPathThat(
        includes: <Offset>[
          const Offset(50.0, 70.0),
          const Offset(50.0, 75.0),
          const Offset(55.0, 70.0),
          const Offset(70.0, 70.0),
          const Offset(70.0, 71.0),
          const Offset(71.0, 70.0),
          const Offset(71.0, 71.0),
          const Offset(80.0, 180.0),
          const Offset(80.0, 190.0),
          const Offset(89.0, 189.0),
          const Offset(90.0, 190.0),
        ],
        excludes: <Offset>[
          const Offset(50.0, 50.0),
          const Offset(40.0, 60.0),
          const Offset(50.0, 60.0),
          const Offset(60.0, 60.0),
          const Offset(0.0, 0.0),
          const Offset(-10.0, -10.0),
          const Offset(0.0, -10.0),
          const Offset(-10.0, 0.0),
          const Offset(110.0, 80.0),
          const Offset(89.0, 191.0),
          const Offset(90.0, 191.0),
          const Offset(91.0, 189.0),
          const Offset(91.0, 190.0),
          const Offset(91.0, 191.0),
          const Offset(109.0, 189.0),
          const Offset(110.0, 190.0),
          const Offset(1000.0, 1000.0),
        ],
      ),
    );
  }, skip: isBrowser);

  test('BorderDirectional constructor', () {
    expect(() => BorderDirectional(top: nonconst(null)), throwsAssertionError);
    expect(() => BorderDirectional(start: nonconst(null)), throwsAssertionError);
    expect(() => BorderDirectional(end: nonconst(null)), throwsAssertionError);
    expect(() => BorderDirectional(bottom: nonconst(null)), throwsAssertionError);
  }, skip: isBrowser);

  test('BorderDirectional.merge', () {
    const BorderSide magenta3 = BorderSide(color: Color(0xFFFF00FF), width: 3.0);
    const BorderSide magenta6 = BorderSide(color: Color(0xFFFF00FF), width: 6.0);
    const BorderSide yellow2 = BorderSide(color: Color(0xFFFFFF00), width: 2.0);
    const BorderSide yellowNone0 = BorderSide(color: Color(0xFFFFFF00), width: 0.0, style: BorderStyle.none);
    expect(
      BorderDirectional.merge(
        const BorderDirectional(top: yellow2),
        const BorderDirectional(end: magenta3),
      ),
      const BorderDirectional(top: yellow2, end: magenta3),
    );
    expect(
      BorderDirectional.merge(
        const BorderDirectional(bottom: magenta3),
        const BorderDirectional(bottom: magenta3),
      ),
      const BorderDirectional(bottom: magenta6),
    );
    expect(
      BorderDirectional.merge(
        const BorderDirectional(start: magenta3, end: yellowNone0),
        const BorderDirectional(end: yellow2),
      ),
      const BorderDirectional(start: magenta3, end: yellow2),
    );
    expect(
      BorderDirectional.merge(const BorderDirectional(), const BorderDirectional()),
      const BorderDirectional(),
    );
    expect(
      () => BorderDirectional.merge(
        const BorderDirectional(start: magenta3),
        const BorderDirectional(start: yellow2),
      ),
      throwsAssertionError,
    );
  }, skip: isBrowser);

  test('BorderDirectional.dimensions', () {
    expect(
      const BorderDirectional(
        top: BorderSide(width: 3.0),
        start: BorderSide(width: 2.0),
        end: BorderSide(width: 7.0),
        bottom: BorderSide(width: 5.0),
      ).dimensions,
      const EdgeInsetsDirectional.fromSTEB(2.0, 3.0, 7.0, 5.0),
    );
  }, skip: isBrowser);

  test('BorderDirectional.isUniform', () {
    expect(
      const BorderDirectional(
        top: BorderSide(width: 3.0),
        start: BorderSide(width: 3.0),
        end: BorderSide(width: 3.0),
        bottom: BorderSide(width: 3.1),
      ).isUniform,
      false,
    );
    expect(
      const BorderDirectional(
        top: BorderSide(width: 3.0),
        start: BorderSide(width: 3.0),
        end: BorderSide(width: 3.0),
        bottom: BorderSide(width: 3.0),
      ).isUniform,
      true,
    );
    expect(
      const BorderDirectional(
        top: BorderSide(color: Color(0xFFFFFFFF)),
        start: BorderSide(color: Color(0xFFFFFFFE)),
        end: BorderSide(color: Color(0xFFFFFFFF)),
        bottom: BorderSide(color: Color(0xFFFFFFFF)),
      ).isUniform,
      false,
    );
    expect(
      const BorderDirectional(
        top: BorderSide(color: Color(0xFFFFFFFF)),
        start: BorderSide(color: Color(0xFFFFFFFF)),
        end: BorderSide(color: Color(0xFFFFFFFF)),
        bottom: BorderSide(color: Color(0xFFFFFFFF)),
      ).isUniform,
      true,
    );
    expect(
      const BorderDirectional(
        top: BorderSide(style: BorderStyle.none),
        start: BorderSide(style: BorderStyle.none),
        end: BorderSide(style: BorderStyle.none),
        bottom: BorderSide(style: BorderStyle.solid, width: 0.0),
      ).isUniform,
      false,
    );
    expect(
      const BorderDirectional(
        top: BorderSide(style: BorderStyle.none),
        start: BorderSide(style: BorderStyle.none),
        end: BorderSide(style: BorderStyle.none),
        bottom: BorderSide.none,
      ).isUniform,
      false,
    );
    expect(
      const BorderDirectional(
        top: BorderSide(style: BorderStyle.none, width: 0.0),
        start: BorderSide(style: BorderStyle.none, width: 0.0),
        end: BorderSide(style: BorderStyle.none, width: 0.0),
        bottom: BorderSide.none,
      ).isUniform,
      true,
    );
    expect(
      const BorderDirectional().isUniform,
      true,
    );
  }, skip: isBrowser);

  test('BorderDirectional.add - all directional', () {
    const BorderSide magenta3 = BorderSide(color: Color(0xFFFF00FF), width: 3.0);
    const BorderSide magenta6 = BorderSide(color: Color(0xFFFF00FF), width: 6.0);
    const BorderSide yellow2 = BorderSide(color: Color(0xFFFFFF00), width: 2.0);
    const BorderSide yellowNone0 = BorderSide(color: Color(0xFFFFFF00), width: 0.0, style: BorderStyle.none);
    expect(
      const BorderDirectional(top: yellow2) + const BorderDirectional(end: magenta3),
      const BorderDirectional(top: yellow2, end: magenta3),
    );
    expect(
      const BorderDirectional(bottom: magenta3) + const BorderDirectional(bottom: magenta3),
      const BorderDirectional(bottom: magenta6),
    );
    expect(
      const BorderDirectional(start: magenta3, end: yellowNone0) + const BorderDirectional(end: yellow2),
      const BorderDirectional(start: magenta3, end: yellow2),
    );
    expect(
      const BorderDirectional() + const BorderDirectional(),
      const BorderDirectional(),
    );
    expect(
      const BorderDirectional(start: magenta3) + const BorderDirectional(start: yellow2),
      isNot(isA<BorderDirectional>()), // see shape_border_test.dart for better tests of this case
    );
    const BorderDirectional b3 = BorderDirectional(top: magenta3);
    const BorderDirectional b6 = BorderDirectional(top: magenta6);
    expect(b3 + b3, b6);
    const BorderDirectional b0 = BorderDirectional(top: yellowNone0);
    const BorderDirectional bZ = BorderDirectional();
    expect(b0 + b0, bZ);
    expect(bZ + bZ, bZ);
    expect(b0 + bZ, bZ);
    expect(bZ + b0, bZ);
  }, skip: isBrowser);

  test('BorderDirectional.add', () {
    const BorderSide side1 = BorderSide(color: Color(0x11111111));
    const BorderSide doubleSide1 = BorderSide(color: Color(0x11111111), width: 2.0);
    const BorderSide side2 = BorderSide(color: Color(0x22222222));
    const BorderSide doubleSide2 = BorderSide(color: Color(0x22222222), width: 2.0);

    // adding tops and sides
    expect(const Border(left: side1) + const BorderDirectional(top: side2), const Border(left: side1, top: side2));
    expect(const BorderDirectional(start: side1) + const Border(top: side2), const BorderDirectional(start: side1, top: side2));
    expect(const Border(top: side2) + const BorderDirectional(start: side1), const BorderDirectional(start: side1, top: side2));
    expect(const BorderDirectional(top: side2) + const Border(left: side1), const Border(left: side1, top: side2));

    // adding incompatible tops and bottoms
    expect((const Border(top: side1) + const BorderDirectional(top: side2)).toString(), contains(' + '));
    expect((const BorderDirectional(top: side2) + const Border(top: side1)).toString(), contains(' + '));
    expect((const Border(bottom: side1) + const BorderDirectional(bottom: side2)).toString(), contains(' + '));
    expect((const BorderDirectional(bottom: side2) + const Border(bottom: side1)).toString(), contains(' + '));

    // adding compatible tops and bottoms
    expect(const BorderDirectional(top: side1) + const Border(top: side1), const Border(top: doubleSide1));
    expect(const Border(top: side1) + const BorderDirectional(top: side1), const Border(top: doubleSide1));
    expect(const BorderDirectional(bottom: side1) + const Border(bottom: side1), const Border(bottom: doubleSide1));
    expect(const Border(bottom: side1) + const BorderDirectional(bottom: side1), const Border(bottom: doubleSide1));

    const Border borderWithLeft = Border(left: side1, top: side2, bottom: side2);
    const Border borderWithRight = Border(right: side1, top: side2, bottom: side2);
    const Border borderWithoutSides = Border(top: side2, bottom: side2);
    const BorderDirectional borderDirectionalWithStart = BorderDirectional(start: side1, top: side2, bottom: side2);
    const BorderDirectional borderDirectionalWithEnd = BorderDirectional(end: side1, top: side2, bottom: side2);
    const BorderDirectional borderDirectionalWithoutSides = BorderDirectional(top: side2, bottom: side2);

    expect((borderWithLeft + borderDirectionalWithStart).toString(), '$borderWithLeft + $borderDirectionalWithStart');
    expect((borderWithLeft + borderDirectionalWithEnd).toString(), '$borderWithLeft + $borderDirectionalWithEnd');
    expect((borderWithLeft + borderDirectionalWithoutSides).toString(), '${const Border(left: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderWithRight + borderDirectionalWithStart).toString(), '$borderWithRight + $borderDirectionalWithStart');
    expect((borderWithRight + borderDirectionalWithEnd).toString(), '$borderWithRight + $borderDirectionalWithEnd');
    expect((borderWithRight + borderDirectionalWithoutSides).toString(), '${const Border(right: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderWithoutSides + borderDirectionalWithStart).toString(), '${const BorderDirectional(start: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderWithoutSides + borderDirectionalWithEnd).toString(), '${const BorderDirectional(end: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderWithoutSides + borderDirectionalWithoutSides).toString(), '${const Border(top: doubleSide2, bottom: doubleSide2)}');

    expect((borderDirectionalWithStart + borderWithLeft).toString(), '$borderDirectionalWithStart + $borderWithLeft');
    expect((borderDirectionalWithEnd + borderWithLeft).toString(), '$borderDirectionalWithEnd + $borderWithLeft');
    expect((borderDirectionalWithoutSides + borderWithLeft).toString(), '${const Border(left: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderDirectionalWithStart + borderWithRight).toString(), '$borderDirectionalWithStart + $borderWithRight');
    expect((borderDirectionalWithEnd + borderWithRight).toString(), '$borderDirectionalWithEnd + $borderWithRight');
    expect((borderDirectionalWithoutSides + borderWithRight).toString(), '${const Border(right: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderDirectionalWithStart + borderWithoutSides).toString(), '${const BorderDirectional(start: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderDirectionalWithEnd + borderWithoutSides).toString(), '${const BorderDirectional(end: side1, top: doubleSide2, bottom: doubleSide2)}');
    expect((borderDirectionalWithoutSides + borderWithoutSides).toString(), '${const Border(top: doubleSide2, bottom: doubleSide2)}');
  }, skip: isBrowser);

  test('BorderDirectional.scale', () {
    const BorderSide magenta3 = BorderSide(color: Color(0xFFFF00FF), width: 3.0);
    const BorderSide magenta6 = BorderSide(color: Color(0xFFFF00FF), width: 6.0);
    const BorderSide yellow2 = BorderSide(color: Color(0xFFFFFF00), width: 2.0);
    const BorderSide yellowNone0 = BorderSide(color: Color(0xFFFFFF00), width: 0.0, style: BorderStyle.none);
    const BorderDirectional b3 = BorderDirectional(start: magenta3);
    const BorderDirectional b6 = BorderDirectional(start: magenta6);
    expect(b3.scale(2.0), b6);
    const BorderDirectional bY0 = BorderDirectional(top: yellowNone0);
    expect(bY0.scale(3.0), bY0);
    const BorderDirectional bY2 = BorderDirectional(top: yellow2);
    expect(bY2.scale(0.0), bY0);
  }, skip: isBrowser);

  test('BorderDirectional.lerp', () {
    const BorderDirectional directionalWithTop10 = BorderDirectional(top: BorderSide(width: 10.0));
    const BorderDirectional atMinus100 = BorderDirectional(start: BorderSide(width: 0.0), end: BorderSide(width: 300.0));
    const BorderDirectional at0 = BorderDirectional(start: BorderSide(width: 100.0), end: BorderSide(width: 200.0));
    const BorderDirectional at25 = BorderDirectional(start: BorderSide(width: 125.0), end: BorderSide(width: 175.0));
    const BorderDirectional at75 = BorderDirectional(start: BorderSide(width: 175.0), end: BorderSide(width: 125.0));
    const BorderDirectional at100 = BorderDirectional(start: BorderSide(width: 200.0), end: BorderSide(width: 100.0));
    const BorderDirectional at200 = BorderDirectional(start: BorderSide(width: 300.0), end: BorderSide(width: 0.0));

    expect(BorderDirectional.lerp(null, null, -1.0), null);
    expect(BorderDirectional.lerp(directionalWithTop10, null, -1.0), const BorderDirectional(top: BorderSide(width: 20.0)));
    expect(BorderDirectional.lerp(null, directionalWithTop10, -1.0), const BorderDirectional());
    expect(BorderDirectional.lerp(at0, at100, -1.0), atMinus100);

    expect(BorderDirectional.lerp(null, null, 0.0), null);
    expect(BorderDirectional.lerp(directionalWithTop10, null, 0.0), const BorderDirectional(top: BorderSide(width: 10.0)));
    expect(BorderDirectional.lerp(null, directionalWithTop10, 0.0), const BorderDirectional());
    expect(BorderDirectional.lerp(at0, at100, 0.0), at0);

    expect(BorderDirectional.lerp(null, null, 0.25), null);
    expect(BorderDirectional.lerp(directionalWithTop10, null, 0.25), const BorderDirectional(top: BorderSide(width: 7.5)));
    expect(BorderDirectional.lerp(null, directionalWithTop10, 0.25), const BorderDirectional(top: BorderSide(width: 2.5)));
    expect(BorderDirectional.lerp(at0, at100, 0.25), at25);

    expect(BorderDirectional.lerp(null, null, 0.75), null);
    expect(BorderDirectional.lerp(directionalWithTop10, null, 0.75), const BorderDirectional(top: BorderSide(width: 2.5)));
    expect(BorderDirectional.lerp(null, directionalWithTop10, 0.75), const BorderDirectional(top: BorderSide(width: 7.5)));
    expect(BorderDirectional.lerp(at0, at100, 0.75), at75);

    expect(BorderDirectional.lerp(null, null, 1.0), null);
    expect(BorderDirectional.lerp(directionalWithTop10, null, 1.0), const BorderDirectional());
    expect(BorderDirectional.lerp(null, directionalWithTop10, 1.0), const BorderDirectional(top: BorderSide(width: 10.0)));
    expect(BorderDirectional.lerp(at0, at100, 1.0), at100);

    expect(BorderDirectional.lerp(null, null, 2.0), null);
    expect(BorderDirectional.lerp(directionalWithTop10, null, 2.0), const BorderDirectional());
    expect(BorderDirectional.lerp(null, directionalWithTop10, 2.0), const BorderDirectional(top: BorderSide(width: 20.0)));
    expect(BorderDirectional.lerp(at0, at100, 2.0), at200);
  });

  test('BorderDirectional.paint', () {
    expect(
      (Canvas canvas) {
        const BorderDirectional(end: BorderSide(width: 10.0, color: Color(0xFF00FF00)))
          .paint(canvas, const Rect.fromLTRB(10.0, 20.0, 30.0, 40.0), textDirection: TextDirection.rtl);
      },
      paints
        ..path(
          includes: <Offset>[const Offset(15.0, 30.0)],
          excludes: <Offset>[const Offset(25.0, 30.0)],
          color: const Color(0xFF00FF00),
        ),
    );
    expect(
      (Canvas canvas) {
        const BorderDirectional(end: BorderSide(width: 10.0, color: Color(0xFF00FF00)))
          .paint(canvas, const Rect.fromLTRB(10.0, 20.0, 30.0, 40.0), textDirection: TextDirection.ltr);
      },
      paints
        ..path(
          includes: <Offset>[const Offset(25.0, 30.0)],
          excludes: <Offset>[const Offset(15.0, 30.0)],
          color: const Color(0xFF00FF00),
        ),
    );
    expect(
      (Canvas canvas) {
        const BorderDirectional(end: BorderSide(width: 10.0, color: Color(0xFF00FF00)))
          .paint(canvas, const Rect.fromLTRB(10.0, 20.0, 30.0, 40.0));
      },
      paintsAssertion, // no TextDirection
    );
  }, skip: isBrowser);

  test('BorderDirectional hashCode', () {
    final BorderSide side = BorderSide(width: nonconst(2.0));
    expect(BorderDirectional(top: side).hashCode, BorderDirectional(top: side).hashCode);
    expect(BorderDirectional(top: side).hashCode, isNot(BorderDirectional(bottom: side).hashCode));
  });

  test('BoxDecoration.border takes a BorderDirectional', () {
    const BoxDecoration decoration2 = BoxDecoration(
      border: BorderDirectional(start: BorderSide(width: 2.0)),
    );
    const BoxDecoration decoration4 = BoxDecoration(
      border: BorderDirectional(start: BorderSide(width: 4.0)),
    );
    const BoxDecoration decoration6 = BoxDecoration(
      border: BorderDirectional(start: BorderSide(width: 6.0)),
    );
    final BoxPainter painter = decoration2.createBoxPainter();
    expect(
      (Canvas canvas) {
        painter.paint(
          canvas,
          const Offset(30.0, 0.0),
          const ImageConfiguration(size: Size(20.0, 20.0), textDirection: TextDirection.rtl),
        );
      },
      paints
        ..path(
          includes: <Offset>[const Offset(49.0, 10.0)],
          excludes: <Offset>[const Offset(31.0, 10.0)],
        ),
    );
    expect(
      (Canvas canvas) {
        painter.paint(
          canvas,
          const Offset(30.0, 0.0),
          const ImageConfiguration(size: Size(20.0, 20.0), textDirection: TextDirection.ltr),
        );
      },
      paints
        ..path(
          includes: <Offset>[const Offset(31.0, 10.0)],
          excludes: <Offset>[const Offset(49.0, 10.0)],
        ),
    );
    expect(decoration2.padding, const EdgeInsetsDirectional.fromSTEB(2.0, 0.0, 0.0, 0.0));
    expect(decoration2.scale(2.0), decoration4);
    expect(BoxDecoration.lerp(decoration2, decoration6, 0.5), decoration4);
  }, skip: isBrowser);
}
