Consolidate common code between the two type schema environment tests.

Change-Id: I014f6024372e57577026f0a1f2dda905899c3b00
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241200
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
index ea04c62..dadd9ee 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_nnbd_test.dart
@@ -2,16 +2,12 @@
 // 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:front_end/src/fasta/type_inference/type_constraint_gatherer.dart';
-import 'package:front_end/src/fasta/type_inference/type_schema.dart';
-import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
 import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
-import 'package:kernel/class_hierarchy.dart';
-import 'package:kernel/testing/type_parser_environment.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'type_schema_environment_test_base.dart';
+
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(TypeSchemaEnvironmentTest);
@@ -19,47 +15,9 @@
 }
 
 @reflectiveTest
-class TypeSchemaEnvironmentTest {
-  late Env typeParserEnvironment;
-  late TypeSchemaEnvironment typeSchemaEnvironment;
-
-  final Map<String, DartType Function()> additionalTypes = {
-    "UNKNOWN": () => new UnknownType(),
-  };
-
-  late Library _coreLibrary;
-  late Library _testLibrary;
-
-  Library get coreLibrary => _coreLibrary;
-  Library get testLibrary => _testLibrary;
-
-  Component get component => typeParserEnvironment.component;
-  CoreTypes get coreTypes => typeParserEnvironment.coreTypes;
-
-  void parseTestLibrary(String testLibraryText) {
-    typeParserEnvironment =
-        new Env(testLibraryText, isNonNullableByDefault: true);
-    typeSchemaEnvironment = new TypeSchemaEnvironment(
-        coreTypes, new ClassHierarchy(component, coreTypes));
-    assert(
-        typeParserEnvironment.component.libraries.length == 2,
-        "The tests are supposed to have exactly two libraries: "
-        "the core library and the test library.");
-    Library firstLibrary = typeParserEnvironment.component.libraries.first;
-    Library secondLibrary = typeParserEnvironment.component.libraries.last;
-    if (firstLibrary.importUri.isScheme("dart") &&
-        firstLibrary.importUri.path == "core") {
-      _coreLibrary = firstLibrary;
-      _testLibrary = secondLibrary;
-    } else {
-      assert(
-          secondLibrary.importUri.isScheme("dart") &&
-              secondLibrary.importUri.path == "core",
-          "One of the libraries is expected to be 'dart:core'.");
-      _coreLibrary == secondLibrary;
-      _testLibrary = firstLibrary;
-    }
-  }
+class TypeSchemaEnvironmentTest extends TypeSchemaEnvironmentTestBase {
+  @override
+  bool get isNonNullableByDefault => true;
 
   void test_addLowerBound() {
     parseTestLibrary("""
@@ -1503,68 +1461,6 @@
     checkIsSubtype("Pair<A*, Null>*", "Pair<UNKNOWN, UNKNOWN>*");
   }
 
-  void checkConstraintSolving(String constraint, String expected,
-      {required bool grounded}) {
-    expect(
-        typeSchemaEnvironment.solveTypeConstraint(
-            parseConstraint(constraint),
-            coreTypes.objectNullableRawType,
-            new NeverType.internal(Nullability.nonNullable),
-            grounded: grounded),
-        parseType(expected));
-  }
-
-  void checkConstraintUpperBound(
-      {required String constraint, required String bound}) {
-    expect(parseConstraint(constraint).upper, parseType(bound));
-  }
-
-  void checkConstraintLowerBound(
-      {required String constraint, required String bound}) {
-    expect(parseConstraint(constraint).lower, parseType(bound));
-  }
-
-  void checkTypeSatisfiesConstraint(String type, String constraint) {
-    expect(
-        typeSchemaEnvironment.typeSatisfiesConstraint(
-            parseType(type), parseConstraint(constraint)),
-        isTrue);
-  }
-
-  void checkTypeDoesntSatisfyConstraint(String type, String constraint) {
-    expect(
-        typeSchemaEnvironment.typeSatisfiesConstraint(
-            parseType(type), parseConstraint(constraint)),
-        isFalse);
-  }
-
-  void checkIsSubtype(String subtype, String supertype) {
-    expect(
-        typeSchemaEnvironment
-            .performNullabilityAwareSubtypeCheck(
-                parseType(subtype), parseType(supertype))
-            .isSubtypeWhenUsingNullabilities(),
-        isTrue);
-  }
-
-  void checkIsLegacySubtype(String subtype, String supertype) {
-    expect(
-        typeSchemaEnvironment
-            .performNullabilityAwareSubtypeCheck(
-                parseType(subtype), parseType(supertype))
-            .isSubtypeWhenIgnoringNullabilities(),
-        isTrue);
-  }
-
-  void checkIsNotSubtype(String subtype, String supertype) {
-    expect(
-        typeSchemaEnvironment
-            .performNullabilityAwareSubtypeCheck(
-                parseType(subtype), parseType(supertype))
-            .isSubtypeWhenIgnoringNullabilities(),
-        isFalse);
-  }
-
   void checkUpperBound(
       {required String type1,
       required String type2,
@@ -1588,150 +1484,4 @@
           parseType(upperBound));
     });
   }
-
-  void checkLowerBound(
-      {required String type1,
-      required String type2,
-      required String lowerBound,
-      String? typeParameters}) {
-    typeParserEnvironment.withTypeParameters(typeParameters,
-        (List<TypeParameter> typeParameterNodes) {
-      expect(
-          typeSchemaEnvironment.getStandardLowerBound(
-              parseType(type1), parseType(type2), testLibrary),
-          parseType(lowerBound));
-    });
-  }
-
-  void checkInference(
-      {required String typeParametersToInfer,
-      required String functionType,
-      String? actualParameterTypes,
-      String? returnContextType,
-      String? inferredTypesFromDownwardPhase,
-      required String expectedTypes}) {
-    typeParserEnvironment.withTypeParameters(typeParametersToInfer,
-        (List<TypeParameter> typeParameterNodesToInfer) {
-      FunctionType functionTypeNode = parseType(functionType) as FunctionType;
-      DartType? returnContextTypeNode =
-          returnContextType == null ? null : parseType(returnContextType);
-      List<DartType>? actualTypeNodes = actualParameterTypes == null
-          ? null
-          : parseTypes(actualParameterTypes);
-      List<DartType> expectedTypeNodes = parseTypes(expectedTypes);
-      DartType declaredReturnTypeNode = functionTypeNode.returnType;
-      List<DartType>? formalTypeNodes = actualParameterTypes == null
-          ? null
-          : functionTypeNode.positionalParameters;
-
-      List<DartType> inferredTypeNodes;
-      if (inferredTypesFromDownwardPhase == null) {
-        inferredTypeNodes = new List<DartType>.generate(
-            typeParameterNodesToInfer.length, (_) => new UnknownType());
-      } else {
-        inferredTypeNodes = parseTypes(inferredTypesFromDownwardPhase);
-      }
-
-      TypeConstraintGatherer gatherer =
-          typeSchemaEnvironment.setupGenericTypeInference(
-              declaredReturnTypeNode,
-              typeParameterNodesToInfer,
-              returnContextTypeNode,
-              testLibrary);
-      if (formalTypeNodes == null) {
-        typeSchemaEnvironment.downwardsInfer(gatherer,
-            typeParameterNodesToInfer, inferredTypeNodes, testLibrary);
-      } else {
-        gatherer.constrainArguments(formalTypeNodes, actualTypeNodes!);
-        typeSchemaEnvironment.upwardsInfer(gatherer, typeParameterNodesToInfer,
-            inferredTypeNodes, testLibrary);
-      }
-
-      assert(
-          inferredTypeNodes.length == expectedTypeNodes.length,
-          "The numbers of expected types and type parameters to infer "
-          "mismatch.");
-      for (int i = 0; i < inferredTypeNodes.length; ++i) {
-        expect(inferredTypeNodes[i], expectedTypeNodes[i]);
-      }
-    });
-  }
-
-  void checkInferenceFromConstraints(
-      {required String typeParameter,
-      required String constraints,
-      String? inferredTypeFromDownwardPhase,
-      required bool downwardsInferPhase,
-      required String expected}) {
-    assert(inferredTypeFromDownwardPhase == null || !downwardsInferPhase);
-
-    typeParserEnvironment.withTypeParameters(typeParameter,
-        (List<TypeParameter> typeParameterNodes) {
-      assert(typeParameterNodes.length == 1);
-
-      TypeConstraint typeConstraint = parseConstraint(constraints);
-      DartType expectedTypeNode = parseType(expected);
-      TypeParameter typeParameterNode = typeParameterNodes.single;
-      List<DartType> inferredTypeNodes = <DartType>[
-        inferredTypeFromDownwardPhase == null
-            ? new UnknownType()
-            : parseType(inferredTypeFromDownwardPhase)
-      ];
-
-      typeSchemaEnvironment.inferTypeFromConstraints(
-          {typeParameterNode: typeConstraint},
-          [typeParameterNode],
-          inferredTypeNodes,
-          testLibrary,
-          downwardsInferPhase: downwardsInferPhase);
-
-      expect(inferredTypeNodes.single, expectedTypeNode);
-    });
-  }
-
-  /// Parses a string like "<: T <: S >: R" into a [TypeConstraint].
-  ///
-  /// The [constraint] string is assumed to be a sequence of bounds added to the
-  /// constraint.  Each element of the sequence is either "<: T" or ":> T",
-  /// where the former adds an upper bound and the latter adds a lower bound.
-  /// The bounds are added to the constraint in the order they are mentioned in
-  /// the [constraint] string, from left to right.
-  TypeConstraint parseConstraint(String constraint) {
-    TypeConstraint result = new TypeConstraint();
-    List<String> upperBoundSegments = constraint.split("<:");
-    bool firstUpperBoundSegment = true;
-    for (String upperBoundSegment in upperBoundSegments) {
-      if (firstUpperBoundSegment) {
-        firstUpperBoundSegment = false;
-        if (upperBoundSegment.isEmpty) {
-          continue;
-        }
-      }
-      List<String> lowerBoundSegments = upperBoundSegment.split(":>");
-      bool firstLowerBoundSegment = true;
-      for (String segment in lowerBoundSegments) {
-        if (firstLowerBoundSegment) {
-          firstLowerBoundSegment = false;
-          if (segment.isNotEmpty) {
-            typeSchemaEnvironment.addUpperBound(
-                result, parseType(segment), testLibrary);
-          }
-        } else {
-          typeSchemaEnvironment.addLowerBound(
-              result, parseType(segment), testLibrary);
-        }
-      }
-    }
-    return result;
-  }
-
-  DartType parseType(String type) {
-    return typeParserEnvironment.parseType(type,
-        additionalTypes: additionalTypes);
-  }
-
-  List<DartType> parseTypes(String types) {
-    return typeParserEnvironment.parseTypes(types,
-        additionalTypes: additionalTypes);
-  }
 }
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart
index f603242..d45112f 100644
--- a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test.dart
@@ -2,16 +2,12 @@
 // 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:front_end/src/fasta/type_inference/type_constraint_gatherer.dart';
-import 'package:front_end/src/fasta/type_inference/type_schema.dart';
-import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
 import 'package:kernel/ast.dart';
-import 'package:kernel/core_types.dart';
-import 'package:kernel/class_hierarchy.dart';
-import 'package:kernel/testing/type_parser_environment.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'type_schema_environment_test_base.dart';
+
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(TypeSchemaEnvironmentTest);
@@ -19,47 +15,9 @@
 }
 
 @reflectiveTest
-class TypeSchemaEnvironmentTest {
-  late Env typeParserEnvironment;
-  late TypeSchemaEnvironment typeSchemaEnvironment;
-
-  final Map<String, DartType Function()> additionalTypes = {
-    "UNKNOWN": () => new UnknownType(),
-  };
-
-  late Library _coreLibrary;
-  late Library _testLibrary;
-
-  Library get coreLibrary => _coreLibrary;
-  Library get testLibrary => _testLibrary;
-
-  Component get component => typeParserEnvironment.component;
-  CoreTypes get coreTypes => typeParserEnvironment.coreTypes;
-
-  void parseTestLibrary(String testLibraryText) {
-    typeParserEnvironment =
-        new Env(testLibraryText, isNonNullableByDefault: false);
-    typeSchemaEnvironment = new TypeSchemaEnvironment(
-        coreTypes, new ClassHierarchy(component, coreTypes));
-    assert(
-        typeParserEnvironment.component.libraries.length == 2,
-        "The tests are supposed to have exactly two libraries: "
-        "the core library and the test library.");
-    Library firstLibrary = typeParserEnvironment.component.libraries.first;
-    Library secondLibrary = typeParserEnvironment.component.libraries.last;
-    if (firstLibrary.importUri.isScheme("dart") &&
-        firstLibrary.importUri.path == "core") {
-      _coreLibrary = firstLibrary;
-      _testLibrary = secondLibrary;
-    } else {
-      assert(
-          secondLibrary.importUri.isScheme("dart") &&
-              secondLibrary.importUri.path == "core",
-          "One of the libraries is expected to be 'dart:core'.");
-      _coreLibrary == secondLibrary;
-      _testLibrary = firstLibrary;
-    }
-  }
+class TypeSchemaEnvironmentTest extends TypeSchemaEnvironmentTestBase {
+  @override
+  bool get isNonNullableByDefault => false;
 
   void test_addLowerBound() {
     parseTestLibrary("class A; class B extends A; class C extends A;");
@@ -585,66 +543,6 @@
     checkIsLegacySubtype("Map<A*, A*>*", "Map<UNKNOWN, UNKNOWN>*");
   }
 
-  void checkConstraintSolving(String constraint, String expected,
-      {required bool grounded}) {
-    expect(
-        typeSchemaEnvironment.solveTypeConstraint(
-            parseConstraint(constraint), new DynamicType(), new NullType(),
-            grounded: grounded),
-        parseType(expected));
-  }
-
-  void checkConstraintUpperBound(
-      {required String constraint, required String bound}) {
-    expect(parseConstraint(constraint).upper, parseType(bound));
-  }
-
-  void checkConstraintLowerBound(
-      {required String constraint, required String bound}) {
-    expect(parseConstraint(constraint).lower, parseType(bound));
-  }
-
-  void checkTypeSatisfiesConstraint(String type, String constraint) {
-    expect(
-        typeSchemaEnvironment.typeSatisfiesConstraint(
-            parseType(type), parseConstraint(constraint)),
-        isTrue);
-  }
-
-  void checkTypeDoesntSatisfyConstraint(String type, String constraint) {
-    expect(
-        typeSchemaEnvironment.typeSatisfiesConstraint(
-            parseType(type), parseConstraint(constraint)),
-        isFalse);
-  }
-
-  void checkIsSubtype(String subtype, String supertype) {
-    expect(
-        typeSchemaEnvironment
-            .performNullabilityAwareSubtypeCheck(
-                parseType(subtype), parseType(supertype))
-            .isSubtypeWhenUsingNullabilities(),
-        isTrue);
-  }
-
-  void checkIsLegacySubtype(String subtype, String supertype) {
-    expect(
-        typeSchemaEnvironment
-            .performNullabilityAwareSubtypeCheck(
-                parseType(subtype), parseType(supertype))
-            .isSubtypeWhenIgnoringNullabilities(),
-        isTrue);
-  }
-
-  void checkIsNotSubtype(String subtype, String supertype) {
-    expect(
-        typeSchemaEnvironment
-            .performNullabilityAwareSubtypeCheck(
-                parseType(subtype), parseType(supertype))
-            .isSubtypeWhenIgnoringNullabilities(),
-        isFalse);
-  }
-
   void checkUpperBound(
       {required String type1,
       required String type2,
@@ -658,150 +556,4 @@
           parseType(upperBound));
     });
   }
-
-  void checkLowerBound(
-      {required String type1,
-      required String type2,
-      required String lowerBound,
-      String? typeParameters}) {
-    typeParserEnvironment.withTypeParameters(typeParameters,
-        (List<TypeParameter> typeParameterNodes) {
-      expect(
-          typeSchemaEnvironment.getStandardLowerBound(
-              parseType(type1), parseType(type2), testLibrary),
-          parseType(lowerBound));
-    });
-  }
-
-  void checkInference(
-      {required String typeParametersToInfer,
-      required String functionType,
-      String? actualParameterTypes,
-      String? returnContextType,
-      String? inferredTypesFromDownwardPhase,
-      required String expectedTypes}) {
-    typeParserEnvironment.withTypeParameters(typeParametersToInfer,
-        (List<TypeParameter> typeParameterNodesToInfer) {
-      FunctionType functionTypeNode = parseType(functionType) as FunctionType;
-      DartType? returnContextTypeNode =
-          returnContextType == null ? null : parseType(returnContextType);
-      List<DartType>? actualTypeNodes = actualParameterTypes == null
-          ? null
-          : parseTypes(actualParameterTypes);
-      List<DartType> expectedTypeNodes = parseTypes(expectedTypes);
-      DartType declaredReturnTypeNode = functionTypeNode.returnType;
-      List<DartType>? formalTypeNodes = actualParameterTypes == null
-          ? null
-          : functionTypeNode.positionalParameters;
-
-      List<DartType> inferredTypeNodes;
-      if (inferredTypesFromDownwardPhase == null) {
-        inferredTypeNodes = new List<DartType>.generate(
-            typeParameterNodesToInfer.length, (_) => new UnknownType());
-      } else {
-        inferredTypeNodes = parseTypes(inferredTypesFromDownwardPhase);
-      }
-
-      TypeConstraintGatherer gatherer =
-          typeSchemaEnvironment.setupGenericTypeInference(
-              declaredReturnTypeNode,
-              typeParameterNodesToInfer,
-              returnContextTypeNode,
-              testLibrary);
-      if (formalTypeNodes == null) {
-        typeSchemaEnvironment.downwardsInfer(gatherer,
-            typeParameterNodesToInfer, inferredTypeNodes, testLibrary);
-      } else {
-        gatherer.constrainArguments(formalTypeNodes, actualTypeNodes!);
-        typeSchemaEnvironment.upwardsInfer(gatherer, typeParameterNodesToInfer,
-            inferredTypeNodes, testLibrary);
-      }
-
-      assert(
-          inferredTypeNodes.length == expectedTypeNodes.length,
-          "The numbers of expected types and type parameters to infer "
-          "mismatch.");
-      for (int i = 0; i < inferredTypeNodes.length; ++i) {
-        expect(inferredTypeNodes[i], expectedTypeNodes[i]);
-      }
-    });
-  }
-
-  void checkInferenceFromConstraints(
-      {required String typeParameter,
-      required String constraints,
-      String? inferredTypeFromDownwardPhase,
-      required bool downwardsInferPhase,
-      required String expected}) {
-    assert(inferredTypeFromDownwardPhase == null || !downwardsInferPhase);
-
-    typeParserEnvironment.withTypeParameters(typeParameter,
-        (List<TypeParameter> typeParameterNodes) {
-      assert(typeParameterNodes.length == 1);
-
-      TypeConstraint typeConstraint = parseConstraint(constraints);
-      DartType expectedTypeNode = parseType(expected);
-      TypeParameter typeParameterNode = typeParameterNodes.single;
-      List<DartType> inferredTypeNodes = <DartType>[
-        inferredTypeFromDownwardPhase == null
-            ? new UnknownType()
-            : parseType(inferredTypeFromDownwardPhase)
-      ];
-
-      typeSchemaEnvironment.inferTypeFromConstraints(
-          {typeParameterNode: typeConstraint},
-          [typeParameterNode],
-          inferredTypeNodes,
-          testLibrary,
-          downwardsInferPhase: downwardsInferPhase);
-
-      expect(inferredTypeNodes.single, expectedTypeNode);
-    });
-  }
-
-  /// Parses a string like "<: T <: S >: R" into a [TypeConstraint].
-  ///
-  /// The [constraint] string is assumed to be a sequence of bounds added to the
-  /// constraint.  Each element of the sequence is either "<: T" or ":> T",
-  /// where the former adds an upper bound and the latter adds a lower bound.
-  /// The bounds are added to the constraint in the order they are mentioned in
-  /// the [constraint] string, from left to right.
-  TypeConstraint parseConstraint(String constraint) {
-    TypeConstraint result = new TypeConstraint();
-    List<String> upperBoundSegments = constraint.split("<:");
-    bool firstUpperBoundSegment = true;
-    for (String upperBoundSegment in upperBoundSegments) {
-      if (firstUpperBoundSegment) {
-        firstUpperBoundSegment = false;
-        if (upperBoundSegment.isEmpty) {
-          continue;
-        }
-      }
-      List<String> lowerBoundSegments = upperBoundSegment.split(":>");
-      bool firstLowerBoundSegment = true;
-      for (String segment in lowerBoundSegments) {
-        if (firstLowerBoundSegment) {
-          firstLowerBoundSegment = false;
-          if (segment.isNotEmpty) {
-            typeSchemaEnvironment.addUpperBound(
-                result, parseType(segment), testLibrary);
-          }
-        } else {
-          typeSchemaEnvironment.addLowerBound(
-              result, parseType(segment), testLibrary);
-        }
-      }
-    }
-    return result;
-  }
-
-  DartType parseType(String type) {
-    return typeParserEnvironment.parseType(type,
-        additionalTypes: additionalTypes);
-  }
-
-  List<DartType> parseTypes(String types) {
-    return typeParserEnvironment.parseTypes(types,
-        additionalTypes: additionalTypes);
-  }
 }
diff --git a/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart
new file mode 100644
index 0000000..2e9bf18
--- /dev/null
+++ b/pkg/front_end/test/fasta/type_inference/type_schema_environment_test_base.dart
@@ -0,0 +1,269 @@
+// Copyright (c) 2022, 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:front_end/src/fasta/type_inference/type_constraint_gatherer.dart';
+import 'package:front_end/src/fasta/type_inference/type_schema.dart';
+import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/core_types.dart';
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/testing/type_parser_environment.dart';
+import 'package:test/test.dart';
+
+abstract class TypeSchemaEnvironmentTestBase {
+  bool get isNonNullableByDefault;
+
+  late Env typeParserEnvironment;
+  late TypeSchemaEnvironment typeSchemaEnvironment;
+
+  final Map<String, DartType Function()> additionalTypes = {
+    "UNKNOWN": () => new UnknownType(),
+  };
+
+  late Library _coreLibrary;
+  late Library _testLibrary;
+
+  Library get coreLibrary => _coreLibrary;
+  Library get testLibrary => _testLibrary;
+
+  Component get component => typeParserEnvironment.component;
+  CoreTypes get coreTypes => typeParserEnvironment.coreTypes;
+
+  void parseTestLibrary(String testLibraryText) {
+    typeParserEnvironment = new Env(testLibraryText,
+        isNonNullableByDefault: isNonNullableByDefault);
+    typeSchemaEnvironment = new TypeSchemaEnvironment(
+        coreTypes, new ClassHierarchy(component, coreTypes));
+    assert(
+        typeParserEnvironment.component.libraries.length == 2,
+        "The tests are supposed to have exactly two libraries: "
+        "the core library and the test library.");
+    Library firstLibrary = typeParserEnvironment.component.libraries.first;
+    Library secondLibrary = typeParserEnvironment.component.libraries.last;
+    if (firstLibrary.importUri.isScheme("dart") &&
+        firstLibrary.importUri.path == "core") {
+      _coreLibrary = firstLibrary;
+      _testLibrary = secondLibrary;
+    } else {
+      assert(
+          secondLibrary.importUri.isScheme("dart") &&
+              secondLibrary.importUri.path == "core",
+          "One of the libraries is expected to be 'dart:core'.");
+      _coreLibrary == secondLibrary;
+      _testLibrary = firstLibrary;
+    }
+  }
+
+  void checkConstraintSolving(String constraint, String expected,
+      {required bool grounded}) {
+    expect(
+        typeSchemaEnvironment.solveTypeConstraint(
+            parseConstraint(constraint),
+            isNonNullableByDefault
+                ? coreTypes.objectNullableRawType
+                : new DynamicType(),
+            isNonNullableByDefault
+                ? new NeverType.internal(Nullability.nonNullable)
+                : new NullType(),
+            grounded: grounded),
+        parseType(expected));
+  }
+
+  void checkConstraintUpperBound(
+      {required String constraint, required String bound}) {
+    expect(parseConstraint(constraint).upper, parseType(bound));
+  }
+
+  void checkConstraintLowerBound(
+      {required String constraint, required String bound}) {
+    expect(parseConstraint(constraint).lower, parseType(bound));
+  }
+
+  void checkTypeSatisfiesConstraint(String type, String constraint) {
+    expect(
+        typeSchemaEnvironment.typeSatisfiesConstraint(
+            parseType(type), parseConstraint(constraint)),
+        isTrue);
+  }
+
+  void checkTypeDoesntSatisfyConstraint(String type, String constraint) {
+    expect(
+        typeSchemaEnvironment.typeSatisfiesConstraint(
+            parseType(type), parseConstraint(constraint)),
+        isFalse);
+  }
+
+  void checkIsSubtype(String subtype, String supertype) {
+    expect(
+        typeSchemaEnvironment
+            .performNullabilityAwareSubtypeCheck(
+                parseType(subtype), parseType(supertype))
+            .isSubtypeWhenUsingNullabilities(),
+        isTrue);
+  }
+
+  void checkIsLegacySubtype(String subtype, String supertype) {
+    expect(
+        typeSchemaEnvironment
+            .performNullabilityAwareSubtypeCheck(
+                parseType(subtype), parseType(supertype))
+            .isSubtypeWhenIgnoringNullabilities(),
+        isTrue);
+  }
+
+  void checkIsNotSubtype(String subtype, String supertype) {
+    expect(
+        typeSchemaEnvironment
+            .performNullabilityAwareSubtypeCheck(
+                parseType(subtype), parseType(supertype))
+            .isSubtypeWhenIgnoringNullabilities(),
+        isFalse);
+  }
+
+  void checkLowerBound(
+      {required String type1,
+      required String type2,
+      required String lowerBound,
+      String? typeParameters}) {
+    typeParserEnvironment.withTypeParameters(typeParameters,
+        (List<TypeParameter> typeParameterNodes) {
+      expect(
+          typeSchemaEnvironment.getStandardLowerBound(
+              parseType(type1), parseType(type2), testLibrary),
+          parseType(lowerBound));
+    });
+  }
+
+  void checkInference(
+      {required String typeParametersToInfer,
+      required String functionType,
+      String? actualParameterTypes,
+      String? returnContextType,
+      String? inferredTypesFromDownwardPhase,
+      required String expectedTypes}) {
+    typeParserEnvironment.withTypeParameters(typeParametersToInfer,
+        (List<TypeParameter> typeParameterNodesToInfer) {
+      FunctionType functionTypeNode = parseType(functionType) as FunctionType;
+      DartType? returnContextTypeNode =
+          returnContextType == null ? null : parseType(returnContextType);
+      List<DartType>? actualTypeNodes = actualParameterTypes == null
+          ? null
+          : parseTypes(actualParameterTypes);
+      List<DartType> expectedTypeNodes = parseTypes(expectedTypes);
+      DartType declaredReturnTypeNode = functionTypeNode.returnType;
+      List<DartType>? formalTypeNodes = actualParameterTypes == null
+          ? null
+          : functionTypeNode.positionalParameters;
+
+      List<DartType> inferredTypeNodes;
+      if (inferredTypesFromDownwardPhase == null) {
+        inferredTypeNodes = new List<DartType>.generate(
+            typeParameterNodesToInfer.length, (_) => new UnknownType());
+      } else {
+        inferredTypeNodes = parseTypes(inferredTypesFromDownwardPhase);
+      }
+
+      TypeConstraintGatherer gatherer =
+          typeSchemaEnvironment.setupGenericTypeInference(
+              declaredReturnTypeNode,
+              typeParameterNodesToInfer,
+              returnContextTypeNode,
+              testLibrary);
+      if (formalTypeNodes == null) {
+        typeSchemaEnvironment.downwardsInfer(gatherer,
+            typeParameterNodesToInfer, inferredTypeNodes, testLibrary);
+      } else {
+        gatherer.constrainArguments(formalTypeNodes, actualTypeNodes!);
+        typeSchemaEnvironment.upwardsInfer(gatherer, typeParameterNodesToInfer,
+            inferredTypeNodes, testLibrary);
+      }
+
+      assert(
+          inferredTypeNodes.length == expectedTypeNodes.length,
+          "The numbers of expected types and type parameters to infer "
+          "mismatch.");
+      for (int i = 0; i < inferredTypeNodes.length; ++i) {
+        expect(inferredTypeNodes[i], expectedTypeNodes[i]);
+      }
+    });
+  }
+
+  void checkInferenceFromConstraints(
+      {required String typeParameter,
+      required String constraints,
+      String? inferredTypeFromDownwardPhase,
+      required bool downwardsInferPhase,
+      required String expected}) {
+    assert(inferredTypeFromDownwardPhase == null || !downwardsInferPhase);
+
+    typeParserEnvironment.withTypeParameters(typeParameter,
+        (List<TypeParameter> typeParameterNodes) {
+      assert(typeParameterNodes.length == 1);
+
+      TypeConstraint typeConstraint = parseConstraint(constraints);
+      DartType expectedTypeNode = parseType(expected);
+      TypeParameter typeParameterNode = typeParameterNodes.single;
+      List<DartType> inferredTypeNodes = <DartType>[
+        inferredTypeFromDownwardPhase == null
+            ? new UnknownType()
+            : parseType(inferredTypeFromDownwardPhase)
+      ];
+
+      typeSchemaEnvironment.inferTypeFromConstraints(
+          {typeParameterNode: typeConstraint},
+          [typeParameterNode],
+          inferredTypeNodes,
+          testLibrary,
+          downwardsInferPhase: downwardsInferPhase);
+
+      expect(inferredTypeNodes.single, expectedTypeNode);
+    });
+  }
+
+  /// Parses a string like "<: T <: S >: R" into a [TypeConstraint].
+  ///
+  /// The [constraint] string is assumed to be a sequence of bounds added to the
+  /// constraint.  Each element of the sequence is either "<: T" or ":> T",
+  /// where the former adds an upper bound and the latter adds a lower bound.
+  /// The bounds are added to the constraint in the order they are mentioned in
+  /// the [constraint] string, from left to right.
+  TypeConstraint parseConstraint(String constraint) {
+    TypeConstraint result = new TypeConstraint();
+    List<String> upperBoundSegments = constraint.split("<:");
+    bool firstUpperBoundSegment = true;
+    for (String upperBoundSegment in upperBoundSegments) {
+      if (firstUpperBoundSegment) {
+        firstUpperBoundSegment = false;
+        if (upperBoundSegment.isEmpty) {
+          continue;
+        }
+      }
+      List<String> lowerBoundSegments = upperBoundSegment.split(":>");
+      bool firstLowerBoundSegment = true;
+      for (String segment in lowerBoundSegments) {
+        if (firstLowerBoundSegment) {
+          firstLowerBoundSegment = false;
+          if (segment.isNotEmpty) {
+            typeSchemaEnvironment.addUpperBound(
+                result, parseType(segment), testLibrary);
+          }
+        } else {
+          typeSchemaEnvironment.addLowerBound(
+              result, parseType(segment), testLibrary);
+        }
+      }
+    }
+    return result;
+  }
+
+  DartType parseType(String type) {
+    return typeParserEnvironment.parseType(type,
+        additionalTypes: additionalTypes);
+  }
+
+  List<DartType> parseTypes(String types) {
+    return typeParserEnvironment.parseTypes(types,
+        additionalTypes: additionalTypes);
+  }
+}