| // Copyright (c) 2019, 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:nnbd_migration/instrumentation.dart'; | 
 | import 'package:nnbd_migration/src/edge_origin.dart'; | 
 | import 'package:nnbd_migration/src/nullability_node.dart'; | 
 | import 'package:nnbd_migration/src/nullability_node_target.dart'; | 
 | import 'package:test/test.dart'; | 
 | import 'package:test_reflective_loader/test_reflective_loader.dart'; | 
 |  | 
 | main() { | 
 |   defineReflectiveSuite(() { | 
 |     defineReflectiveTests(NullabilityNodeTest); | 
 |   }); | 
 | } | 
 |  | 
 | @reflectiveTest | 
 | class NullabilityNodeTest { | 
 |   final graph = NullabilityGraphForTesting(); | 
 |  | 
 |   /// A list of all edges that couldn't be satisfied.  May contain duplicates. | 
 |   List<NullabilityEdge> unsatisfiedEdges; | 
 |  | 
 |   List<NullabilityNodeForSubstitution> unsatisfiedSubstitutions; | 
 |  | 
 |   NullabilityNode get always => graph.always; | 
 |  | 
 |   NullabilityNode get never => graph.never; | 
 |  | 
 |   void assertUnsatisfied(List<NullabilityEdge> expectedUnsatisfiedEdges) { | 
 |     expect(unsatisfiedEdges.toSet(), expectedUnsatisfiedEdges.toSet()); | 
 |   } | 
 |  | 
 |   NullabilityEdge connect(NullabilityNode source, NullabilityNode destination, | 
 |       {bool hard = false, | 
 |       bool checkable = true, | 
 |       List<NullabilityNode> guards = const []}) { | 
 |     return graph.connect(source, destination, _TestEdgeOrigin(), | 
 |         hard: hard, checkable: checkable, guards: guards); | 
 |   } | 
 |  | 
 |   NullabilityNode lub(NullabilityNode left, NullabilityNode right) { | 
 |     return NullabilityNode.forLUB(left, right); | 
 |   } | 
 |  | 
 |   NullabilityNode newNode(int id) => | 
 |       NullabilityNode.forTypeAnnotation(NullabilityNodeTarget.text('node $id')); | 
 |  | 
 |   void propagate() { | 
 |     var propagationResult = graph.propagate(null); | 
 |     unsatisfiedEdges = propagationResult.unsatisfiedEdges; | 
 |     unsatisfiedSubstitutions = propagationResult.unsatisfiedSubstitutions; | 
 |   } | 
 |  | 
 |   NullabilityNode subst(NullabilityNode inner, NullabilityNode outer) { | 
 |     return NullabilityNode.forSubstitution(inner, outer); | 
 |   } | 
 |  | 
 |   void test_always_and_never_state() { | 
 |     propagate(); | 
 |     expect(always.isNullable, isTrue); | 
 |     expect(never.isNullable, isFalse); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_always_and_never_unaffected_by_hard_edges() { | 
 |     var edge = connect(always, never, hard: true); | 
 |     propagate(); | 
 |     expect(always.isNullable, isTrue); | 
 |     expect(never.isNullable, isFalse); | 
 |     assertUnsatisfied([edge]); | 
 |   } | 
 |  | 
 |   void test_always_and_never_unaffected_by_soft_edges() { | 
 |     var edge = connect(always, never); | 
 |     propagate(); | 
 |     expect(always.isNullable, isTrue); | 
 |     expect(never.isNullable, isFalse); | 
 |     assertUnsatisfied([edge]); | 
 |   } | 
 |  | 
 |   void test_always_destination() { | 
 |     // always -> 1 -(hard)-> always | 
 |     var n1 = newNode(1); | 
 |     connect(always, n1); | 
 |     connect(n1, always, hard: true); | 
 |     propagate(); | 
 |     // Upstream propagation of non-nullability ignores edges terminating at | 
 |     // `always`, so n1 should be nullable. | 
 |     expect(n1.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_edge_satisfied_due_to_guard() { | 
 |     // always -> 1 | 
 |     // 1 -(2) -> 3 | 
 |     // 3 -(hard)-> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(n1, n3, guards: [n2]); | 
 |     connect(n3, never, hard: true); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, false); | 
 |     // Although n1 is nullable and n3 is non-nullable, the edge from 1 to 3 is | 
 |     // considered satisfied because the guard (n2) is non-nullable. | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_lubNode_relatesInBothDirections() { | 
 |     final nodeA = newNode(1); | 
 |     final nodeB = newNode(2); | 
 |     final lubNode = lub(nodeA, nodeB); | 
 |  | 
 |     expect(nodeA.outerCompoundNodes, [lubNode]); | 
 |     expect(nodeB.outerCompoundNodes, [lubNode]); | 
 |   } | 
 |  | 
 |   void test_never_source() { | 
 |     // never -> 1 | 
 |     var n1 = newNode(1); | 
 |     connect(never, n1); | 
 |     propagate(); | 
 |     // Downstream propagation of nullability ignores edges originating at | 
 |     // `never`, so n1 should be non-nullable. | 
 |     expect(n1.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_always_union() { | 
 |     // always == 1 | 
 |     // 1 -(hard)-> never | 
 |     // 1 -> 2 | 
 |     // 1 -> 3 | 
 |     // 3 -(hard)-> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     union(always, n1); | 
 |     var edge_1_never = connect(n1, never, hard: true); | 
 |     connect(n1, n2); | 
 |     var edge_1_3 = connect(n1, n3); | 
 |     connect(n3, never, hard: true); | 
 |     propagate(); | 
 |     // Union edges take precedence over hard ones, so n1 should be nullable. | 
 |     expect(n1.isNullable, true); | 
 |     // And nullability should be propagated to n2. | 
 |     expect(n2.isNullable, true); | 
 |     // But it should not be propagated to n3 because non-nullability propagation | 
 |     // takes precedence over ordinary nullability propagation. | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([edge_1_never, edge_1_3]); | 
 |   } | 
 |  | 
 |   void test_propagation_always_union_reversed() { | 
 |     // always == 1 | 
 |     // 1 -(hard)-> never | 
 |     // 1 -> 2 | 
 |     // 1 -> 3 | 
 |     // 3 -(hard)-> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     union(n1, always); | 
 |     var edge_1_never = connect(n1, never, hard: true); | 
 |     connect(n1, n2); | 
 |     var edge_1_3 = connect(n1, n3); | 
 |     connect(n3, never, hard: true); | 
 |     propagate(); | 
 |     // Union edges take precedence over hard ones, so n1 should be nullable. | 
 |     expect(n1.isNullable, true); | 
 |     // And nullability should be propagated to n2. | 
 |     expect(n2.isNullable, true); | 
 |     // But it should not be propagated to n3 because non-nullability propagation | 
 |     // takes precedence over ordinary nullability propagation. | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([edge_1_never, edge_1_3]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_breadth_first() { | 
 |     // always -> 1 -> 2 | 
 |     //           1 -> 3 -> 4 | 
 |     // always -> 5 -> 4 | 
 |     //           5 -> 6 -> 2 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(4); | 
 |     var n5 = newNode(5); | 
 |     var n6 = newNode(6); | 
 |     connect(always, n1); | 
 |     connect(n1, n2); | 
 |     connect(n1, n3); | 
 |     connect(n3, n4); | 
 |     connect(always, n5); | 
 |     connect(n5, n4); | 
 |     connect(n5, n6); | 
 |     connect(n6, n2); | 
 |     propagate(); | 
 |     // Node 2 should be caused by node 1, since that's the shortest path back to | 
 |     // "always".  Similarly, node 4 should be caused by node 5. | 
 |     expect(_downstreamCauseNode(n2), same(n1)); | 
 |     expect(_downstreamCauseNode(n4), same(n5)); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_guarded_multiple_guards_all_satisfied() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // always -> 3 | 
 |     // 1 -(2,3)-> 4 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(always, n2); | 
 |     connect(always, n3); | 
 |     connect(n1, n4, guards: [n2, n3]); | 
 |     propagate(); | 
 |     expect(n4.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_guarded_multiple_guards_not_all_satisfied() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // 1 -(2,3)-> 4 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(always, n2); | 
 |     connect(n1, n4, guards: [n2, n3]); | 
 |     propagate(); | 
 |     expect(n4.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_guarded_satisfy_guard_first() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // 2 -(1)-> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(always, n2); | 
 |     connect(n2, n3, guards: [n1]); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_guarded_satisfy_source_first() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // 1 -(2)-> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(always, n2); | 
 |     connect(n1, n3, guards: [n2]); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_guarded_unsatisfied_guard() { | 
 |     // always -> 1 | 
 |     // 1 -(2)-> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(n1, n3, guards: [n2]); | 
 |     propagate(); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_guarded_unsatisfied_source() { | 
 |     // always -> 1 | 
 |     // 2 -(1)-> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(n2, n3, guards: [n1]); | 
 |     propagate(); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_reverse_substitution_exact() { | 
 |     // always -> subst(1, 2) | 
 |     // 3 -(uncheckable)-> 1 | 
 |     // 4 -(uncheckable)-> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(4); | 
 |     connect(always, subst(n1, n2)); | 
 |     connect(n3, n1, checkable: false); | 
 |     connect(n4, n3, checkable: false); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n1.isExactNullable, true); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, true); | 
 |     expect(n3.isExactNullable, true); | 
 |     expect(n4.isNullable, true); | 
 |     expect(n4.isExactNullable, true); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_reverse_substitution_exact_checkable() { | 
 |     // always -> subst(1, 2) | 
 |     // 3 -(uncheckable)-> 1 | 
 |     // 4 -(checkable)-> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(4); | 
 |     connect(always, subst(n1, n2)); | 
 |     connect(n3, n1, checkable: false); | 
 |     connect(n4, n3, checkable: true); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n1.isExactNullable, true); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, true); | 
 |     expect(n3.isExactNullable, true); | 
 |     expect(n4.isNullable, false); | 
 |     expect(n4.isExactNullable, false); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_reverse_substitution_inner_non_nullable() { | 
 |     // 1 -> never (hard) | 
 |     // always -> subst(1, 2) | 
 |     // 3 -> 2 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(n1, never, hard: true); | 
 |     connect(always, subst(n1, n2)); | 
 |     connect(n3, n2); | 
 |     propagate(); | 
 |     expect(n1.isNullable, false); | 
 |     expect(n2.isNullable, true); | 
 |     expect(n2.isExactNullable, false); | 
 |     expect(n3.isNullable, false); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_reverse_substitution_non_null_intent() { | 
 |     // always -> subst(1, 2) | 
 |     // 3 -(uncheckable)-> 1 | 
 |     // 4 -(checkable)-> 3 | 
 |     // 4 -(hard) -> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(4); | 
 |     connect(always, subst(n1, n2)); | 
 |     connect(n3, n1, checkable: false); | 
 |     connect(n4, n3, checkable: false); | 
 |     connect(n4, never, hard: true); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n1.isExactNullable, true); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, true); | 
 |     expect(n3.isExactNullable, true); | 
 |     expect(n4.isNullable, false); | 
 |     expect(n4.isExactNullable, false); | 
 |   } | 
 |  | 
 |   void | 
 |       test_propagation_downstream_reverse_substitution_outer_already_nullable() { | 
 |     // always -> 2 | 
 |     // always -> subst(1, 2) | 
 |     // 3 -> 2 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n2); | 
 |     connect(always, subst(n1, n2)); | 
 |     connect(n3, n2); | 
 |     propagate(); | 
 |     expect(n1.isNullable, false); | 
 |     expect(n2.isNullable, true); | 
 |     expect(n2.isExactNullable, false); | 
 |     expect(n3.isNullable, false); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_reverse_substitution_unsatisfiable() { | 
 |     // 1 -> never (hard) | 
 |     // 2 -> never (hard) | 
 |     // always -> subst(1, 2) | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     connect(n1, never, hard: true); | 
 |     connect(n2, never, hard: true); | 
 |     var substitutionNode = subst(n1, n2); | 
 |     connect(always, substitutionNode); | 
 |     propagate(); | 
 |     expect(n1.isNullable, false); | 
 |     expect(n2.isNullable, false); | 
 |     expect(substitutionNode.isNullable, false); | 
 |     expect(unsatisfiedSubstitutions, isEmpty); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_lub_both() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // LUB(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(always, n2); | 
 |     connect(lub(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_lub_cascaded() { | 
 |     // always -> 1 | 
 |     // LUB(LUB(1, 2), 3) -> 4 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(lub(lub(n1, n2), n3), n4); | 
 |     propagate(); | 
 |     expect(n4.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_lub_left() { | 
 |     // always -> 1 | 
 |     // LUB(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(lub(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_lub_neither() { | 
 |     // LUB(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(lub(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_lub_right() { | 
 |     // always -> 2 | 
 |     // LUB(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n2); | 
 |     connect(lub(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_substitution_both() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // subst(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(always, n2); | 
 |     connect(subst(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_substitution_cascaded() { | 
 |     // always -> 1 | 
 |     // subst(subst(1, 2), 3) -> 4 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(subst(subst(n1, n2), n3), n4); | 
 |     propagate(); | 
 |     expect(n4.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_substitution_inner() { | 
 |     // always -> 1 | 
 |     // subst(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     connect(subst(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_substitution_neither() { | 
 |     // subst(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(subst(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_substitution_outer() { | 
 |     // always -> 2 | 
 |     // subst(1, 2) -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n2); | 
 |     connect(subst(n1, n2), n3); | 
 |     propagate(); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_union() { | 
 |     // always -> 1 | 
 |     // 1 == 2 | 
 |     // 2 -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     union(n1, n2); | 
 |     connect(n2, n3); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n2.isNullable, true); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_downstream_through_union_reversed() { | 
 |     // always -> 1 | 
 |     // 2 == 1 | 
 |     // 2 -> 3 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     union(n2, n1); | 
 |     connect(n2, n3); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n2.isNullable, true); | 
 |     expect(n3.isNullable, true); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_propagation_simple() { | 
 |     // always -(soft)-> 1 -(soft)-> 2 -(hard) -> 3 -(hard)-> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     connect(always, n1); | 
 |     var edge_1_2 = connect(n1, n2); | 
 |     connect(n2, n3, hard: true); | 
 |     connect(n3, never, hard: true); | 
 |     propagate(); | 
 |     expect(n1.isNullable, true); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([edge_1_2]); | 
 |   } | 
 |  | 
 |   void test_propagation_upstream_breadth_first() { | 
 |     // never <- 1 <- 2 | 
 |     //          1 <- 3 <- 4 | 
 |     // never <- 5 <- 4 | 
 |     //          5 <- 6 <- 2 | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var n4 = newNode(4); | 
 |     var n5 = newNode(5); | 
 |     var n6 = newNode(6); | 
 |     connect(n1, never, hard: true); | 
 |     connect(n2, n1, hard: true); | 
 |     connect(n3, n1, hard: true); | 
 |     connect(n4, n3, hard: true); | 
 |     connect(n5, never, hard: true); | 
 |     connect(n4, n5, hard: true); | 
 |     connect(n6, n5, hard: true); | 
 |     connect(n2, n6, hard: true); | 
 |     propagate(); | 
 |     // Node 2 should be caused by node 1, since that's the shortest path back to | 
 |     // "always".  Similarly, node 4 should be caused by node 5. | 
 |     expect(_upstreamCauseNode(n2), same(n1)); | 
 |     expect(_upstreamCauseNode(n4), same(n5)); | 
 |   } | 
 |  | 
 |   void test_propagation_upstream_through_union() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // always -> 3 | 
 |     // 1 -(hard)-> 2 | 
 |     // 2 == 3 | 
 |     // 3 -(hard)-> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var edge_always_1 = connect(always, n1); | 
 |     var edge_always_2 = connect(always, n2); | 
 |     var edge_always_3 = connect(always, n3); | 
 |     connect(n1, n2, hard: true); | 
 |     union(n2, n3); | 
 |     connect(n3, never, hard: true); | 
 |     propagate(); | 
 |     expect(n1.isNullable, false); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]); | 
 |   } | 
 |  | 
 |   void test_propagation_upstream_through_union_reversed() { | 
 |     // always -> 1 | 
 |     // always -> 2 | 
 |     // always -> 3 | 
 |     // 1 -(hard)-> 2 | 
 |     // 3 == 2 | 
 |     // 3 -(hard)-> never | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(2); | 
 |     var n3 = newNode(3); | 
 |     var edge_always_1 = connect(always, n1); | 
 |     var edge_always_2 = connect(always, n2); | 
 |     var edge_always_3 = connect(always, n3); | 
 |     connect(n1, n2, hard: true); | 
 |     union(n3, n2); | 
 |     connect(n3, never, hard: true); | 
 |     propagate(); | 
 |     expect(n1.isNullable, false); | 
 |     expect(n2.isNullable, false); | 
 |     expect(n3.isNullable, false); | 
 |     assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]); | 
 |   } | 
 |  | 
 |   void test_satisfied_edge_destination_nullable() { | 
 |     var n1 = newNode(1); | 
 |     var edge = connect(always, n1); | 
 |     propagate(); | 
 |     assertUnsatisfied([]); | 
 |     expect(edge.isSatisfied, true); | 
 |   } | 
 |  | 
 |   void test_satisfied_edge_source_non_nullable() { | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(1); | 
 |     var edge = connect(n1, n2); | 
 |     propagate(); | 
 |     assertUnsatisfied([]); | 
 |     expect(edge.isSatisfied, true); | 
 |   } | 
 |  | 
 |   void test_satisfied_edge_two_sources_first_non_nullable() { | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(1); | 
 |     connect(always, n2); | 
 |     var edge = connect(n1, never, guards: [n2]); | 
 |     propagate(); | 
 |     assertUnsatisfied([]); | 
 |     expect(edge.isSatisfied, true); | 
 |   } | 
 |  | 
 |   void test_satisfied_edge_two_sources_second_non_nullable() { | 
 |     var n1 = newNode(1); | 
 |     var n2 = newNode(1); | 
 |     connect(always, n1); | 
 |     var edge = connect(n1, never, guards: [n2]); | 
 |     propagate(); | 
 |     assertUnsatisfied([]); | 
 |     expect(edge.isSatisfied, true); | 
 |   } | 
 |  | 
 |   void test_substitution_simplify_null() { | 
 |     var n1 = newNode(1); | 
 |     expect(subst(null, n1), same(n1)); | 
 |     expect(subst(n1, null), same(n1)); | 
 |   } | 
 |  | 
 |   void test_substitutionNode_relatesInBothDirections() { | 
 |     final nodeA = newNode(1); | 
 |     final nodeB = newNode(2); | 
 |     final substNode = subst(nodeA, nodeB); | 
 |  | 
 |     expect(nodeA.outerCompoundNodes, [substNode]); | 
 |     expect(nodeB.outerCompoundNodes, [substNode]); | 
 |   } | 
 |  | 
 |   void test_unconstrainted_node_non_nullable() { | 
 |     var n1 = newNode(1); | 
 |     propagate(); | 
 |     expect(n1.isNullable, false); | 
 |     assertUnsatisfied([]); | 
 |   } | 
 |  | 
 |   void test_unsatisfied_edge_multiple_sources() { | 
 |     var n1 = newNode(1); | 
 |     connect(always, n1); | 
 |     var edge = connect(always, never, guards: [n1]); | 
 |     propagate(); | 
 |     assertUnsatisfied([edge]); | 
 |     expect(edge.isSatisfied, false); | 
 |   } | 
 |  | 
 |   void test_unsatisfied_edge_single_source() { | 
 |     var edge = connect(always, never); | 
 |     propagate(); | 
 |     assertUnsatisfied([edge]); | 
 |     expect(edge.isSatisfied, false); | 
 |   } | 
 |  | 
 |   void union(NullabilityNode x, NullabilityNode y) { | 
 |     graph.union(x, y, _TestEdgeOrigin()); | 
 |   } | 
 |  | 
 |   NullabilityNode _downstreamCauseNode(NullabilityNode node) => | 
 |       (node.whyNullable as SimpleDownstreamPropagationStep).edge.sourceNode; | 
 |  | 
 |   NullabilityNode _upstreamCauseNode(NullabilityNode node) => | 
 |       node.whyNotNullable.principalCause.node; | 
 | } | 
 |  | 
 | class _TestEdgeOrigin implements EdgeOrigin { | 
 |   @override | 
 |   CodeReference get codeReference => null; | 
 |  | 
 |   @override | 
 |   String get description => 'Test edge'; | 
 |  | 
 |   @override | 
 |   EdgeOriginKind get kind => null; | 
 |  | 
 |   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | 
 | } |