def test_trace_multi(self): # Tests that when multiple distinct failures occur, they are each rendered. rules = [ RootRule(B), TaskRule(D, [Select(B)], nested_raise), TaskRule(C, [Select(B)], nested_raise), TaskRule(A, [Select(C), Select(D)], A), ] scheduler = self.scheduler(rules, include_trace_on_error=True) with self.assertRaises(ExecutionError) as cm: list(scheduler.product_request(A, subjects=[(B())])) self.assert_equal_with_printing( dedent(''' 1 Exception encountered: Computing Select(<pants_test.engine.test_engine.B object at 0xEEEEEEEEE>, =A) Computing Task(A, <pants_test.engine.test_engine.B object at 0xEEEEEEEEE>, =A, true) Computing Task(nested_raise, <pants_test.engine.test_engine.B object at 0xEEEEEEEEE>, =D, true) Throw(An exception for B) Traceback (most recent call last): File LOCATION-INFO, in call val = func(*args) File LOCATION-INFO, in nested_raise fn_raises(x) File LOCATION-INFO, in fn_raises raise Exception('An exception for {}'.format(type(x).__name__)) Exception: An exception for B Computing Select(<pants_test.engine.test_engine.B object at 0xEEEEEEEEE>, =A) Computing Task(A, <pants_test.engine.test_engine.B object at 0xEEEEEEEEE>, =A, true) Computing Task(nested_raise, <pants_test.engine.test_engine.B object at 0xEEEEEEEEE>, =C, true) Throw(An exception for B) Traceback (most recent call last): File LOCATION-INFO, in call val = func(*args) File LOCATION-INFO, in nested_raise fn_raises(x) File LOCATION-INFO, in fn_raises raise Exception('An exception for {}'.format(type(x).__name__)) Exception: An exception for B ''').lstrip() + '\n', remove_locations_from_traceback(str(cm.exception)))
def test_noop_removal_full_single_subject_type(self): rules = [ TaskRule(Exactly(A), [Select(C)], noop), TaskRule(Exactly(A), [], noop), ] fullgraph = self.create_full_graph(_suba_root_subject_types, RuleIndex.create(rules)) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (,), noop) of SubA"} // internal entries "(A, (,), noop) of SubA" -> {} }""").strip(), fullgraph)
def test_no_include_trace_error_raises_boring_error(self): rules = [RootRule(B), TaskRule(A, [Select(B)], nested_raise)] scheduler = self.scheduler(rules, include_trace_on_error=False) with self.assertRaises(Exception) as cm: list(scheduler.product_request(A, subjects=[(B())])) self.assert_equal_with_printing('An exception for B', str(cm.exception))
def test_get_simple(self): rules = [ TaskRule(Exactly(A), [], noop, [Get(B, D)]), TaskRule(B, [Select(D)], noop), ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (,), [Get(B, D)], noop) of SubA"} // internal entries "(A, (,), [Get(B, D)], noop) of SubA" -> {"(B, (Select(D),), noop) of D"} "(B, (Select(D),), noop) of D" -> {"SubjectIsProduct(D)"} }""").strip(), subgraph)
def test_one_level_of_recursion(self): rules = [ TaskRule(Exactly(A), [Select(B)], noop), TaskRule(B, [Select(SubA)], noop) ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (Select(B),), noop) of SubA"} // internal entries "(A, (Select(B),), noop) of SubA" -> {"(B, (Select(SubA),), noop) of SubA"} "(B, (Select(SubA),), noop) of SubA" -> {"SubjectIsProduct(SubA)"} }""").strip(), subgraph)
def test_multiple_selects(self): rules = [ TaskRule(Exactly(A), [Select(SubA), Select(B)], noop), TaskRule(B, [], noop) ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, [Select(SubA), Select(B)], noop) for SubA"} // internal entries "(A, [Select(SubA), Select(B)], noop) for SubA" -> {"(B, [], noop) for ()" "Param(SubA)"} "(B, [], noop) for ()" -> {} }""").strip(), subgraph)
def test_noop_removal_in_subgraph(self): rules = [ TaskRule(Exactly(A), [Select(C)], noop), TaskRule(Exactly(A), [], noop), SingletonRule(B, B()), ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (,), noop) of SubA"} // internal entries "(A, (,), noop) of SubA" -> {} }""").strip(), subgraph)
def test_no_include_trace_error_raises_boring_error(self): rules = [TaskRule(A, [Select(B)], nested_raise)] engine = self.create_engine({B}, rules, include_trace_on_error=False) with self.assertRaises(Exception) as cm: list(engine.product_request(A, subjects=[(B())])) self.assert_equal_with_printing('An exception for B', str(cm.exception))
def test_ruleset_with_ambiguity(self): rules = [ TaskRule(A, [Select(C), Select(B)], noop), TaskRule(A, [Select(B), Select(C)], noop), RootRule(B), RootRule(C), # TODO: Without a rule triggering the selection of A, we don't detect ambiguity here. TaskRule(D, [Select(A)], noop), ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (D, [Select(A)], noop): ambiguous rules for Select(A) with parameter types (B+C): (A, [Select(B), Select(C)], noop) for (B+C) (A, [Select(C), Select(B)], noop) for (B+C) """).strip(), str(cm.exception))
def test_successful_when_one_field_type_is_unfulfillable(self): # NB We may want this to be a warning, since it may not be intentional rules = [ TaskRule(B, [Select(SubA)], noop), TaskRule(D, [Select(Exactly(B)), SelectDependencies(B, SubA, field_types=(SubA, C))], noop) ] subgraph = self.create_subgraph(D, rules, SubA()) self.assert_equal_with_printing(dedent(""" digraph { // root subject types: SubA // root entries "Select(D) for SubA" [color=blue] "Select(D) for SubA" -> {"(D, (Select(B), SelectDependencies(B, SubA, field_types=(SubA, C,))), noop) of SubA"} // internal entries "(B, (Select(SubA),), noop) of SubA" -> {"SubjectIsProduct(SubA)"} "(D, (Select(B), SelectDependencies(B, SubA, field_types=(SubA, C,))), noop) of SubA" -> {"(B, (Select(SubA),), noop) of SubA" "SubjectIsProduct(SubA)"} }""").strip(), subgraph)
def test_select_dependencies_with_subject_as_first_subselector(self): rules = [ TaskRule(Exactly(A), [SelectDependencies(B, SubA, field_types=(D, ))], noop), TaskRule(B, [Select(D)], noop), ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (SelectDependencies(B, SubA, field_types=(D,)),), noop) of SubA"} // internal entries "(A, (SelectDependencies(B, SubA, field_types=(D,)),), noop) of SubA" -> {"SubjectIsProduct(SubA)" "(B, (Select(D),), noop) of D"} "(B, (Select(D),), noop) of D" -> {"SubjectIsProduct(D)"} }""").strip(), subgraph)
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. rules = _suba_root_rules + [ TaskRule(B, [Select(D)], noop), TaskRule(D, [Select(A), Select(SubA)], noop, input_gets=[Get(A, C)]), TaskRule(A, [Select(SubA)], noop) ] with self.assertRaises(ValueError) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (B, (Select(D),), noop): depends on unfulfillable (D, (Select(A), Select(SubA)), [Get(A, C)], noop) of SubA with subject types: SubA (D, (Select(A), Select(SubA)), [Get(A, C)], noop): depends on unfulfillable (A, (Select(SubA),), noop) of C with subject types: SubA """).strip(), str(cm.exception))
def test_depends_on_multiple_one_noop(self): rules = [ TaskRule(B, [Select(A)], noop), TaskRule(A, [Select(C)], noop), TaskRule(A, [Select(SubA)], noop) ] subgraph = self.create_subgraph(B, rules, SubA(), validate=False) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(B) for SubA" [color=blue] "Select(B) for SubA" -> {"(B, [Select(A)], noop) for SubA"} // internal entries "(A, [Select(SubA)], noop) for SubA" -> {"Param(SubA)"} "(B, [Select(A)], noop) for SubA" -> {"(A, [Select(SubA)], noop) for SubA"} }""").strip(), subgraph)
def test_noop_removal_transitive(self): # If a noop-able rule has rules that depend on it, # they should be removed from the graph. rules = [ TaskRule(Exactly(B), [Select(C)], noop), TaskRule(Exactly(A), [Select(B)], noop), TaskRule(Exactly(A), [], noop), ] subgraph = self.create_subgraph(A, rules, SubA(), validate=False) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for ()" [color=blue] "Select(A) for ()" -> {"(A, [], noop) for ()"} // internal entries "(A, [], noop) for ()" -> {} }""").strip(), subgraph)
def test_root_tuple_removed_when_no_matches(self): rules = [ RootRule(C), RootRule(D), TaskRule(Exactly(A), [Select(C)], noop), TaskRule(Exactly(B), [Select(D), Select(A)], noop), ] fullgraph = self.create_full_graph(RuleIndex.create(rules)) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: C, D // root entries "Select(A) for C" [color=blue] "Select(A) for C" -> {"(A, (Select(C),), noop) of C"} // internal entries "(A, (Select(C),), noop) of C" -> {"SubjectIsProduct(C)"} }""").strip(), fullgraph)
def test_ruleset_with_rule_with_two_missing_selects(self): rules = _suba_root_rules + [TaskRule(A, [Select(B), Select(C)], noop)] with self.assertRaises(ValueError) as cm: create_scheduler(rules) self.assert_equal_with_printing(dedent(""" Rules with errors: 1 (A, (Select(B), Select(C)), noop): no matches for Select(B) with subject types: SubA no matches for Select(C) with subject types: SubA """).strip(), str(cm.exception))
def test_select_transitive_with_separate_types_for_subselectors(self): rules = [ TaskRule(Exactly(A), [SelectTransitive(B, C, field_types=(D,))], noop), TaskRule(B, [Select(D)], noop), TaskRule(C, [Select(SubA)], noop) ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing(dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (SelectTransitive(B, C, field_types=(D,)),), noop) of SubA"} // internal entries "(A, (SelectTransitive(B, C, field_types=(D,)),), noop) of SubA" -> {"(C, (Select(SubA),), noop) of SubA" "(B, (Select(D),), noop) of D"} "(B, (Select(D),), noop) of D" -> {"SubjectIsProduct(D)"} "(C, (Select(SubA),), noop) of SubA" -> {"SubjectIsProduct(SubA)"} }""").strip(), subgraph)
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. rules = _suba_root_rules + [ TaskRule(B, [Select(D)], noop), TaskRule(D, [Select(A), SelectDependencies(A, SubA, field_types=(C,))], noop), TaskRule(A, [Select(SubA)], noop) ] validator = self.create_validator({}, rules) with self.assertRaises(ValueError) as cm: validator.assert_ruleset_valid() self.assert_equal_with_printing(dedent(""" Rules with errors: 2 (B, (Select(D),), noop): depends on unfulfillable (D, (Select(A), SelectDependencies(A, SubA, field_types=(C,))), noop) of SubA with subject types: SubA (D, (Select(A), SelectDependencies(A, SubA, field_types=(C,))), noop): depends on unfulfillable (A, (Select(SubA),), noop) of C with subject types: SubA """).strip(), str(cm.exception))
def test_ruleset_with_rule_with_two_missing_selects(self): rules = _suba_root_rules + [TaskRule(A, [Select(B), Select(C)], noop)] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, (Select(B), Select(C)), noop): no rule was available to compute B for subject type SubA no rule was available to compute C for subject type SubA """).strip(), str(cm.exception))
def test_ruleset_with_goal_not_produced(self): # The graph is complete, but the goal 'goal-name' requests A, # which is not produced by any rule. rules = _suba_root_rules + [TaskRule(B, [Select(SubA)], noop)] validator = self.create_validator({'goal-name': AGoal}, rules) with self.assertRaises(ValueError) as cm: validator.assert_ruleset_valid() self.assert_equal_with_printing( "no task for product used by goal \"goal-name\": AGoal", str(cm.exception))
def test_select_dependencies_non_matching_subselector_because_of_singleton(self): rules = [ TaskRule(Exactly(A), [SelectDependencies(B, SubA, field_types=(D,))], noop), SingletonRule(C, C()), ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing(dedent(""" digraph { // empty graph }""").strip(), subgraph)
def test_ruleset_with_missing_product_type(self): rules = _suba_root_rules + [TaskRule(A, [Select(B)], noop)] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, [Select(B)], noop): No rule was available to compute B with parameter type SubA """).strip(), str(cm.exception))
def test_not_fulfillable_duplicated_dependency(self): # If a rule depends on another rule+subject in two ways, and one of them is unfulfillable # Only the unfulfillable one should be in the errors. rules = _suba_root_rules + [ TaskRule(B, [Select(D)], noop), TaskRule( D, [Select(A), Select(SubA)], noop, input_gets=[Get(A, C)]), TaskRule(A, [Select(C)], noop) ] with self.assertRaises(Exception) as cm: create_scheduler(rules) self.assert_equal_with_printing( dedent(""" Rules with errors: 2 (B, [Select(D)], noop): No rule was available to compute D with parameter type SubA (D, [Select(A), Select(SubA)], [Get(A, C)], noop): No rule was available to compute A with parameter type SubA """).strip(), str(cm.exception))
def create_graph_rules(address_mapper, symbol_table_cls): """Creates tasks used to parse Structs from BUILD files. :param address_mapper_key: The subject key for an AddressMapper instance. :param symbol_table_cls: A SymbolTable class to provide symbols for Address lookups. """ symbol_table_constraint = symbol_table_cls.constraint() return [ TaskRule( BuildFilesCollection, [SelectDependencies(BuildFiles, BuildDirs, field_types=(Dir, ))], BuildFilesCollection), # A singleton to provide the AddressMapper. SingletonRule(AddressMapper, address_mapper), # Support for resolving Structs from Addresses. TaskRule(symbol_table_constraint, [ Select(AddressMapper), Select(UnhydratedStruct), SelectDependencies(symbol_table_constraint, UnhydratedStruct, field_types=(Address, )) ], hydrate_struct), resolve_unhydrated_struct, # BUILD file parsing. parse_address_family, build_files, buildfile_path_globs_for_dir, # Spec handling: locate directories that contain build files, and request # AddressFamilies for each of them. addresses_from_address_families, filter_build_dirs, spec_to_globs, # Root rules representing parameters that might be provided via root subjects. RootRule(Address), RootRule(BuildFileAddress), RootRule(AscendantAddresses), RootRule(DescendantAddresses), RootRule(SiblingAddresses), RootRule(SingleAddress), ]
def test_ruleset_with_missing_product_type(self): rules = _suba_root_rules + [TaskRule(A, [Select(B)], noop)] scheduler = create_native_scheduler(rules) with self.assertRaises(ValueError) as cm: scheduler.assert_ruleset_valid() self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, (Select(B),), noop): no matches for Select(B) with subject types: SubA """).strip(), str(cm.exception))
def test_ruleset_with_rule_with_two_missing_selects(self): rules = [TaskRule(A, [Select(B), Select(C)], noop)] validator = self.create_validator({}, {SubA}, rules) with self.assertRaises(ValueError) as cm: validator.assert_ruleset_valid() self.assert_equal_with_printing( dedent(""" Rules with errors: 1 (A, (Select(B), Select(C)), noop): no matches for Select(B) with subject types: SubA no matches for Select(C) with subject types: SubA """).strip(), str(cm.exception))
def test_smallest_full_test(self): rules = [TaskRule(Exactly(A), [Select(SubA)], noop)] fullgraph = self.create_full_graph({SubA}, RuleIndex.create(rules)) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (Select(SubA),), noop) of SubA"} // internal entries "(A, (Select(SubA),), noop) of SubA" -> {"SubjectIsProduct(SubA)"} }""").strip(), fullgraph)
def test_select_dependencies_multiple_field_types_all_resolvable_with_deps(self): rules = [ TaskRule(Exactly(A), [SelectDependencies(B, SubA, field_types=(C, D,))], noop), # for the C type, it'll just be a literal, but for D, it'll traverse one more edge TaskRule(B, [Select(C)], noop), TaskRule(C, [Select(D)], noop), ] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing(dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, (SelectDependencies(B, SubA, field_types=(C, D,)),), noop) of SubA"} // internal entries "(A, (SelectDependencies(B, SubA, field_types=(C, D,)),), noop) of SubA" -> {"SubjectIsProduct(SubA)" "(B, (Select(C),), noop) of C" "(B, (Select(C),), noop) of D"} "(B, (Select(C),), noop) of C" -> {"SubjectIsProduct(C)"} "(B, (Select(C),), noop) of D" -> {"(C, (Select(D),), noop) of D"} "(C, (Select(D),), noop) of D" -> {"SubjectIsProduct(D)"} }""").strip(), subgraph)
def test_root_tuple_removed_when_no_matches(self): rules = [ RootRule(C), RootRule(D), TaskRule(Exactly(A), [Select(C)], noop), TaskRule(Exactly(B), [Select(D), Select(A)], noop), ] fullgraph = self.create_full_graph(rules, validate=False) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: C, D // root entries "Select(A) for C" [color=blue] "Select(A) for C" -> {"(A, [Select(C)], noop) for C"} "Select(B) for (C+D)" [color=blue] "Select(B) for (C+D)" -> {"(B, [Select(D), Select(A)], noop) for (C+D)"} // internal entries "(A, [Select(C)], noop) for C" -> {"Param(C)"} "(B, [Select(D), Select(A)], noop) for (C+D)" -> {"(A, [Select(C)], noop) for C" "Param(D)"} }""").strip(), fullgraph)
def test_single_rule_depending_on_subject_selection(self): rules = [TaskRule(Exactly(A), [Select(SubA)], noop)] subgraph = self.create_subgraph(A, rules, SubA()) self.assert_equal_with_printing( dedent(""" digraph { // root subject types: SubA // root entries "Select(A) for SubA" [color=blue] "Select(A) for SubA" -> {"(A, [Select(SubA)], noop) for SubA"} // internal entries "(A, [Select(SubA)], noop) for SubA" -> {"Param(SubA)"} }""").strip(), subgraph)