diff --git a/docs/unittest_doc.md b/docs/unittest_doc.md index a13b32ad..eb6f2e4a 100755 --- a/docs/unittest_doc.md +++ b/docs/unittest_doc.md @@ -175,23 +175,65 @@ Unconditionally causes the current test to fail. ## analysistest.target_actions
-analysistest.target_actions(env)
+analysistest.target_actions(env, mnemonic)
 
Returns a list of actions registered by the target under test. +If mnemonic is provided, the list of actions will be filtered by +the given mnemonic. The list will be empty if no actions match the +given mnemonic. + + **PARAMETERS** | Name | Description | Default Value | | :------------- | :------------- | :------------- | | env | The test environment returned by analysistest.begin. | none | +| mnemonic | Filter the list of actions by this mnemonic. | None | **RETURNS** A list of actions registered by the target under test + + +## analysistest.target_action + +
+analysistest.target_action(env, mnemonic, output_ending_with)
+
+ +Returns an action registered by the target under test. + +If mnemonic is provided, returns the action with the specified +mnemonic. If no action with the given mnemonic is found, or if +multiple actions with the given mnemonic are found, fail() will +be called. Use target_actions(env, mnemonic = ...) instead. + +If output_ending_with is provided, returns the action with an output +whose path ends with output_ending_with. If no such action is found, +fail() will be called. + +One of mnemonic or output_ending_with must be provided. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| env | The test environment returned by analysistest.begin. | none | +| mnemonic | Finds the action with the given mnemonic. | None | +| output_ending_with | Finds the action with an output that ends with the given suffix. | None | + +**RETURNS** + +An action registered by the target under test + + ## analysistest.target_bin_dir_path diff --git a/lib/unittest.bzl b/lib/unittest.bzl index 4398f164..cc73c691 100644 --- a/lib/unittest.bzl +++ b/lib/unittest.bzl @@ -554,18 +554,78 @@ def _expect_failure(env, expected_failure_msg = ""): else: _fail(env, "Expected failure of target_under_test, but found success") -def _target_actions(env): +def _target_actions(env, mnemonic = None): """Returns a list of actions registered by the target under test. + If mnemonic is provided, the list of actions will be filtered by + the given mnemonic. The list will be empty if no actions match the + given mnemonic. + Args: env: The test environment returned by `analysistest.begin`. + mnemonic: Filter the list of actions by this mnemonic. Returns: A list of actions registered by the target under test """ + actions = _target_under_test(env)[_ActionInfo].actions + + if mnemonic != None: + actions = [a for a in actions if a.mnemonic == mnemonic] + # Validate? - return _target_under_test(env)[_ActionInfo].actions + return actions + +def _target_action(env, mnemonic = None, output_ending_with = None): + """Returns an action registered by the target under test. + + If mnemonic is provided, returns the action with the specified + mnemonic. If no action with the given mnemonic is found, or if + multiple actions with the given mnemonic are found, fail() will + be called. Use target_actions(env, mnemonic = ...) instead. + + If output_ending_with is provided, returns the action with an output + whose path ends with output_ending_with. If no such action is found, + fail() will be called. + + One of mnemonic or output_ending_with must be provided. + + Args: + env: The test environment returned by `analysistest.begin`. + mnemonic: Finds the action with the given mnemonic. + output_ending_with: Finds the action with an output that ends with + the given suffix. + + Returns: + An action registered by the target under test + """ + + if mnemonic == None and output_ending_with == None: + fail("One of mnemonic or output_ending_with must be provided.") + + if mnemonic != None and output_ending_with != None: + fail("Only one of mnemonic or output_ending_with may be provided.") + + actions = _target_under_test(env)[_ActionInfo].actions + + if mnemonic != None: + matching_actions = [] + for action in actions: + if action.mnemonic == mnemonic: + matching_actions.append(action) + if not matching_actions: + fail("No action with mnemonic '%s' found." % mnemonic) + if len(matching_actions) > 1: + fail("Multiple actions with mnemonic '%s' found." % mnemonic) + return matching_actions[0] + + if output_ending_with != None: + for action in actions: + for output in action.outputs.to_list(): + if output.path.endswith(output_ending_with): + return action + fail("No action with an output ending with '%s' found." % output_ending_with) def _target_bin_dir_path(env): """Returns ctx.bin_dir.path for the target under test. @@ -679,6 +739,7 @@ analysistest = struct( end = _end_analysis_test, fail = _fail, target_actions = _target_actions, + target_action = _target_action, target_bin_dir_path = _target_bin_dir_path, target_under_test = _target_under_test, ) diff --git a/tests/BUILD b/tests/BUILD index 7f056d2b..db999acc 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -78,7 +78,9 @@ sh_test( srcs = ["analysis_test_test.sh"], data = [ ":unittest.bash", + "//lib:unittest", "//rules:analysis_test.bzl", + "//toolchains/unittest:test_deps", "@bazel_tools//tools/bash/runfiles", ], tags = ["local"], diff --git a/tests/analysis_test_test.sh b/tests/analysis_test_test.sh index 5d5d7def..b4f335ed 100755 --- a/tests/analysis_test_test.sh +++ b/tests/analysis_test_test.sh @@ -51,6 +51,10 @@ function create_pkg() { cat > WORKSPACE < fakerules/rules.bzl < fakerules/BUILD < testdir/BUILD <"$TEST_log" 2>&1 && fail "Expected test to fail" || true + expect_log "No action with mnemonic 'MnemonicC' found." + + bazel test testdir:inspect_actions_multiple_actions_with_mnemonic_test --test_output=all --verbose_failures \ + >"$TEST_log" 2>&1 && fail "Expected test to fail" || true + expect_log "Multiple actions with mnemonic 'MnemonicA' found." + + bazel test testdir:inspect_actions_no_output_test --test_output=all --verbose_failures \ + >"$TEST_log" 2>&1 && fail "Expected test to fail" || true + expect_log "No action with an output ending with 'foo' found." + + bazel test testdir:inspect_actions_no_filter_test --test_output=all --verbose_failures \ + >"$TEST_log" 2>&1 && fail "Expected test to fail" || true + expect_log "One of mnemonic or output_ending_with must be provided." + + bazel test testdir:inspect_actions_too_many_filters_test --test_output=all --verbose_failures \ + >"$TEST_log" 2>&1 && fail "Expected test to fail" || true + expect_log "Only one of mnemonic or output_ending_with may be provided." +} + cd "$TEST_TMPDIR" run_suite "analysis_test test suite" diff --git a/tests/unittest_tests.bzl b/tests/unittest_tests.bzl index 2f326a01..faf452ff 100644 --- a/tests/unittest_tests.bzl +++ b/tests/unittest_tests.bzl @@ -194,19 +194,65 @@ def _inspect_actions_test(ctx): """Test verifying actions registered by a target.""" env = analysistest.begin(ctx) + # Test getting all actions actions = analysistest.target_actions(env) - asserts.equals(env, 1, len(actions)) + asserts.equals(env, 3, len(actions)) action_output = actions[0].outputs.to_list()[0] - asserts.equals(env, "out.txt", action_output.basename) + asserts.equals(env, "out1.txt", action_output.basename) + + # Test getting a list of 1 action by mnemonic + mnemonic_a_actions = analysistest.target_actions( + env, mnemonic = "MnemonicA") + asserts.equals(env, 1, len(mnemonic_a_actions)) + asserts.equals(env, "MnemonicA", mnemonic_a_actions[0].mnemonic) + + # Test getting multiple actions by mnemonic + mnemonic_b_actions = analysistest.target_actions( + env, mnemonic = "MnemonicB") + asserts.equals(env, 2, len(mnemonic_b_actions)) + asserts.equals(env, "MnemonicB", mnemonic_b_actions[0].mnemonic) + asserts.equals(env, "MnemonicB", mnemonic_b_actions[1].mnemonic) + + # Test getting no actions by mnemonic + mnemonic_c_actions = analysistest.target_actions( + env, mnemonic = "MnemonicC") + asserts.equals(env, 0, len(mnemonic_c_actions)) + + # Test getting a single action by mnemonic + mnemonic_a_action = analysistest.target_action( + env, mnemonic = "MnemonicA") + asserts.equals(env, "MnemonicA", mnemonic_a_action.mnemonic) + + # Test getting a single action by output name + out2_txt_action = analysistest.target_action( + env, output_ending_with = "out2.txt") + asserts.equals( + env, "out2.txt", out2_txt_action.outputs.to_list()[0].basename) + return analysistest.end(env) def _inspect_actions_fake_rule(ctx): - out_file = ctx.actions.declare_file("out.txt") + out1_file = ctx.actions.declare_file("out1.txt") ctx.actions.run_shell( - command = "echo 'hello' > %s" % out_file.basename, - outputs = [out_file], + command = "echo 'hello 1' > %s" % out1_file.basename, + outputs = [out1_file], + mnemonic = "MnemonicA", ) - return [DefaultInfo(files = depset([out_file]))] + out2_file = ctx.actions.declare_file("out2.txt") + ctx.actions.run_shell( + command = "echo 'hello 2' > %s" % out2_file.basename, + outputs = [out2_file], + mnemonic = "MnemonicB", + ) + out3_file = ctx.actions.declare_file("out3.txt") + ctx.actions.run_shell( + command = "echo 'hello 3' > %s" % out3_file.basename, + outputs = [out3_file], + mnemonic = "MnemonicB", # two actions with same mnemonic + ) + return [ + DefaultInfo(files = depset([out1_file, out2_file, out3_file])) + ] inspect_actions_fake_rule = rule( implementation = _inspect_actions_fake_rule,