| // 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); |
| } |