Add support for writing fluent value checks.

Change-Id: Id3bab09fdfd0531ca02c42883159482240a3dfde
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219581
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index a9381bd..f695939 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -21,6 +21,7 @@
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
+import 'package:analyzer_utilities/check/check.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -2794,7 +2795,7 @@
   }
 
   void assertEmpty() {
-    expect(suggestions, isEmpty);
+    check(suggestions).isEmpty;
   }
 
   void assertLength(Object matcher) {
diff --git a/pkg/analyzer_utilities/lib/check/bool.dart b/pkg/analyzer_utilities/lib/check/bool.dart
new file mode 100644
index 0000000..18d5054
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/bool.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check.dart';
+
+extension BoolExtension on CheckTarget<bool> {
+  void get isTrue {
+    if (!value) {
+      fail('is not true');
+    }
+  }
+
+  void get isFalse {
+    if (value) {
+      fail('is not false');
+    }
+  }
+}
diff --git a/pkg/analyzer_utilities/lib/check/check.dart b/pkg/analyzer_utilities/lib/check/check.dart
new file mode 100644
index 0000000..8d3cc4d
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/check.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check_target.dart';
+import 'package:meta/meta.dart';
+
+export 'package:analyzer_utilities/check/check_target.dart';
+export 'package:analyzer_utilities/check/equality.dart';
+export 'package:analyzer_utilities/check/int.dart';
+export 'package:analyzer_utilities/check/iterable.dart';
+export 'package:analyzer_utilities/check/string.dart';
+export 'package:analyzer_utilities/check/type.dart';
+
+@useResult
+CheckTarget<T> check<T>(T value) {
+  return CheckTarget(value, 0, () => '$value');
+}
diff --git a/pkg/analyzer_utilities/lib/check/check_target.dart b/pkg/analyzer_utilities/lib/check/check_target.dart
new file mode 100644
index 0000000..1d0e608
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/check_target.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, 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:test/test.dart' as test_package;
+
+class CheckTarget<T> {
+  final T value;
+  final int _depth;
+
+  /// The function that return the description of the value, and of the
+  /// chain how we arrived to this value.
+  final String Function() _describe;
+
+  CheckTarget(this.value, this._depth, this._describe);
+
+  String get _indent => '  ' * (_depth + 1);
+
+  String valueStr(value) {
+    if (value is String) {
+      return "'$value'";
+    }
+    return '$value';
+  }
+
+  Never fail(String message) {
+    test_package.fail(_describe() + '\n' + _indent + message);
+  }
+
+  /// Chains to the given [value]; if a subsequent check fails, [describe]
+  /// will be invoked to describe the [value].
+  CheckTarget<U> nest<U>(
+    U value,
+    String Function(U value) describe,
+  ) {
+    return CheckTarget(value, _depth + 1, () {
+      return _describe() + '\n' + _indent + describe(value);
+    });
+  }
+}
diff --git a/pkg/analyzer_utilities/lib/check/equality.dart b/pkg/analyzer_utilities/lib/check/equality.dart
new file mode 100644
index 0000000..2d265b8
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/equality.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check.dart';
+
+extension EqualityExtension<T> on CheckTarget<T> {
+  void isEqualTo(Object? other) {
+    if (value != other) {
+      fail('is not equal to $other');
+    }
+  }
+
+  void isNotEqualTo(Object? other) {
+    if (value == other) {
+      fail('is equal to $other');
+    }
+  }
+}
diff --git a/pkg/analyzer_utilities/lib/check/int.dart b/pkg/analyzer_utilities/lib/check/int.dart
new file mode 100644
index 0000000..59587b2
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/int.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check.dart';
+
+extension IntExtension on CheckTarget<int> {
+  void get isZero {
+    if (value != 0) {
+      fail('is not zero');
+    }
+  }
+
+  void isGreaterThan(int other) {
+    if (!(value > other)) {
+      fail('is not greater than $other');
+    }
+  }
+}
diff --git a/pkg/analyzer_utilities/lib/check/iterable.dart b/pkg/analyzer_utilities/lib/check/iterable.dart
new file mode 100644
index 0000000..fbadfd9
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/iterable.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check.dart';
+
+extension IterableExtension<T> on CheckTarget<Iterable<T>> {
+  void get isEmpty {
+    if (value.isNotEmpty) {
+      fail('is not empty');
+    }
+  }
+}
diff --git a/pkg/analyzer_utilities/lib/check/string.dart b/pkg/analyzer_utilities/lib/check/string.dart
new file mode 100644
index 0000000..c4dfdf7
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/string.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check.dart';
+import 'package:meta/meta.dart';
+
+extension StringExtension on CheckTarget<String> {
+  void contains(Pattern other) {
+    if (!value.contains(other)) {
+      fail('does not contain ${valueStr(other)}');
+    }
+  }
+
+  void startsWith(Pattern other) {
+    if (!value.startsWith(other)) {
+      fail('does not start with ${valueStr(other)}');
+    }
+  }
+
+  @useResult
+  CheckTarget<int> hasLength() {
+    return nest(
+      value.length,
+      (length) => 'has length $length',
+    );
+  }
+}
diff --git a/pkg/analyzer_utilities/lib/check/type.dart b/pkg/analyzer_utilities/lib/check/type.dart
new file mode 100644
index 0000000..ea28b9e
--- /dev/null
+++ b/pkg/analyzer_utilities/lib/check/type.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, 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:analyzer_utilities/check/check.dart';
+
+extension IsExtension<T> on CheckTarget<T> {
+  CheckTarget<U> isA<U extends T>() {
+    final value = this.value;
+    if (value is U) {
+      return nest(value, (_) => 'is of type $U');
+    } else {
+      fail('is not of type $U');
+    }
+  }
+}
diff --git a/pkg/analyzer_utilities/pubspec.yaml b/pkg/analyzer_utilities/pubspec.yaml
index a8426e4..d0aac18 100644
--- a/pkg/analyzer_utilities/pubspec.yaml
+++ b/pkg/analyzer_utilities/pubspec.yaml
@@ -9,5 +9,7 @@
   analyzer:
     path: ../analyzer
   html: any
+  meta:
+    path: ../meta
   path: any
   test: any