# Copyright 2023 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for truth.bzl.""" load("@bazel_skylib//lib:unittest.bzl", ut_asserts = "asserts") load("//lib:truth.bzl", "matching", "subjects", "truth") load("//lib:analysis_test.bzl", "analysis_test", "test_suite") # Bazel 5 has a bug where every access of testing.ExecutionInfo is a new # object that isn't equal to itself. This is fixed in Bazel 6. _IS_BAZEL_6_OR_HIGHER = (testing.ExecutionInfo == testing.ExecutionInfo) _suite = [] def _fake_env(env): failures = [] env1 = struct( ctx = env.ctx, failures = failures, fail = lambda msg: failures.append(msg), # Silent fail ) env2 = struct( ctx = env.ctx, failures = failures, fail = lambda msg: failures.append(msg), # Silent fail expect = truth.expect(env1), reset = lambda: failures.clear(), ) return env2 def _end(env, fake_env): _guard_against_stray_failures(env = env, fake_env = fake_env) def _guard_against_stray_failures(*, env, fake_env): ut_asserts.true( env, len(fake_env.failures) == 0, "failures remain: clear after each expected failure\n{}".format( "\n".join(fake_env.failures), ), ) def action_subject_test(name): analysis_test(name, impl = _action_subject_test, target = "truth_tests_helper") def _action_subject_test(env, target): fake_env = _fake_env(env) subject = fake_env.expect.that_target( target, ).action_named("Action1") subject.contains_flag_values([ ("--arg1flag", "arg1value"), ("--arg2flag", "arg2value"), ]) _assert_no_failures( fake_env, env = env, msg = "check contains_flag_values success", ) subject.contains_flag_values([ ("--missingflag", "whatever"), ("--arg1flag", "wrongvalue"), ]) _assert_failure( fake_env, [ "2 expected flags with values missing from argv", "0: '--arg1flag' with value 'wrongvalue'", "1: '--missingflag' (not specified)", "actual argv", "1: arg1", "2: --boolflag", "3: --arg1flag", "4: arg1value", "5: --arg2flag=arg2value", ], env = env, msg = "check contains_flag_values failure", ) subject.contains_none_of_flag_values([ ("--doesnotexist", "whatever"), ("--arg1flag", "differentvalue"), ]) _assert_no_failures( fake_env, env = env, msg = "check contains_none_of_flag_values success", ) subject.contains_none_of_flag_values([ ("--arg1flag", "arg1value"), ]) _assert_failure( fake_env, [ ("expected not to contain any of: \n" + # note space after colon " 0: '--arg1flag' with value 'arg1value'\n"), ("but 1 found:\n" + " 0: '--arg1flag' with value 'arg1value'\n"), "actual values:\n", # Element 0 of actual is omitted because it has build-config # specific values within it. (" 1: arg1\n" + " 2: --boolflag\n" + " 3: --arg1flag\n" + " 4: arg1value\n" + " 5: --arg2flag=arg2value\n"), ], env = env, msg = "check contains_none_of_flag_values failure", ) _end(env, fake_env) _suite.append(action_subject_test) def bool_subject_test(name): analysis_test(name, impl = _bool_subject_test, target = "truth_tests_helper") def _bool_subject_test(env, _target): fake_env = _fake_env(env) fake_env.expect.that_bool(True).equals(True) _assert_no_failures(fake_env, env = env) fake_env.expect.that_bool(False).equals(False) _assert_no_failures(fake_env, env = env) fake_env.expect.that_bool(True).equals(False) _assert_failure(fake_env, [ "expected: False", "actual: True", ], env = env) fake_env.expect.that_bool(True, "MYEXPR").equals(False) _assert_failure(fake_env, ["MYEXPR"], env = env) subject = truth.expect(fake_env).that_bool(True) subject.not_equals(True) _assert_failure( fake_env, ["expected not to be: True", "actual: True"], env = env, msg = "check not_equals fails with same type", ) subject.not_equals(None) _assert_failure( fake_env, ["expected not to be: None (type: NoneType)", "actual: True (type: bool)"], env = env, msg = "check not_equals due to different type", ) subject.not_equals(False) _assert_no_failures( fake_env, env = env, msg = "check BoolSubject.not_equals with unequal value of same type", ) subject.is_in([True, False]) _assert_no_failures( fake_env, env = env, msg = "check BoolSubject.is_in with matching values", ) subject.is_in([None, 39]) _assert_failure( fake_env, ["expected any of:", "None", "39", "actual: True"], env = env, msg = "check is_in mismatched values", ) _end(env, fake_env) _suite.append(bool_subject_test) def collection_custom_expr_test(name): analysis_test(name, impl = _collection_custom_expr_test, target = "truth_tests_helper") def _collection_custom_expr_test(env, _target): fake_env = _fake_env(env) subject = fake_env.expect.that_collection(["a"], "MYEXPR") subject.contains_exactly([]) _assert_failure(fake_env, ["MYEXPR"], env = env) _end(env, fake_env) _suite.append(collection_custom_expr_test) def collection_has_size_test(name): analysis_test(name, impl = _collection_has_size_test, target = "truth_tests_helper") def _collection_has_size_test(env, _target): fake_env = _fake_env(env) subject = fake_env.expect.that_collection(["a", "b", "c", "d"]) subject.has_size(4) _assert_no_failures( fake_env, env = env, msg = "check actual has expected size", ) subject.has_size(0) _assert_failure( fake_env, ["value of: collection.size()"], env = env, msg = "check actual does not have expected size", ) _end(env, fake_env) _suite.append(collection_has_size_test) def collection_contains_test(name): analysis_test(name, impl = _collection_contains_test, target = "truth_tests_helper") def _collection_contains_test(env, _target): fake_env = _fake_env(env) subject = fake_env.expect.that_collection(["a", "b", "c", "d"]) subject.contains("a") _assert_no_failures( fake_env, env = env, msg = "check actual does contain expected", ) subject.contains("never") _assert_failure( fake_env, ["expected to contain: never", "actual values", "0: a"], env = env, msg = "check actual is missing expected", ) _end(env, fake_env) _suite.append(collection_contains_test) def collection_contains_predicate_test(name): analysis_test(name, impl = _collection_contains_predicate_test, target = "truth_tests_helper") def _collection_contains_predicate_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a", "b", "c", "d"]) subject.contains_predicate(matching.contains("a")) _assert_no_failures( fake_env, env = env, msg = "check actual does contains expected", ) subject.contains_predicate(matching.contains("never")) _assert_failure( fake_env, ["expected to contain: ", "actual values", "0: a"], env = env, msg = "check actual is missing a value", ) _end(env, fake_env) _suite.append(collection_contains_predicate_test) def collection_contains_at_least_test(name): analysis_test(name, impl = _collection_contains_at_least_test, target = "truth_tests_helper") def _collection_contains_at_least_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a", "b", "c", "d"]) subject.contains_at_least(["a", "b", "c"]).in_order() _assert_no_failures( fake_env, env = env, msg = "check expected and actual with same elements in same order", ) subject.contains_at_least(["never"]) _assert_failure( fake_env, ["expected elements missing", "never", "actual values", "0: a"], env = env, msg = "check actual is missing a value", ) subject.contains_at_least([ "b", "a", ]).in_order() _assert_failure( fake_env, [ "incorrect order", "0: b found at offset 1", "1: a found at offset 0", ], env = env, msg = "check expected values present in wrong order", ) _end(env, fake_env) _suite.append(collection_contains_at_least_test) def collection_contains_at_least_predicates_test(name): analysis_test(name, impl = _collection_contains_at_least_predicates_test, target = "truth_tests_helper") def _collection_contains_at_least_predicates_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a", "b", "c", "d"]) subject.contains_at_least_predicates([ matching.contains("a"), matching.contains("b"), matching.contains("c"), ]).in_order() subject.contains_at_least_predicates([ matching.never("never"), ]) _assert_failure( fake_env, ["expected elements missing", "never", "actual values", "0: a"], env = env, ) subject.contains_at_least_predicates([ matching.custom("", lambda v: "b" in v), matching.custom("", lambda v: "a" in v), ]).in_order() _assert_failure( fake_env, [ "incorrect order", "0: matched at offset 1 (matched: b)", "1: matched at offset 0 (matched: a)", ], env = env, ) _end(env, fake_env) _suite.append(collection_contains_at_least_predicates_test) def collection_contains_exactly_test(name): analysis_test(name, impl = _collection_contains_exactly_test, target = "truth_tests_helper") def _collection_contains_exactly_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection([]) subject.contains_exactly(["a"]) _assert_failure( fake_env, [ "1 missing:\n 0: a", "expected exactly:\n 0: a", "actual values:\n ", ], env = env, msg = "check empty actual vs non-empty expected", ) subject = truth.expect(fake_env).that_collection(["b"]) subject.contains_exactly([]) _assert_failure( fake_env, [ "1 unexpected:\n 0: b", "expected exactly:\n ", "actual values:\n 0: b", ], env = env, msg = "check non-empty actual vs empty expected", ) subject = truth.expect(fake_env).that_collection(["c"]) order = subject.contains_exactly(["c"]) _assert_no_failures( fake_env, env = env, msg = "check expected and actual with same elements in same order", ) order.in_order() _assert_no_failures( fake_env, env = env, msg = "check exact elements are in order", ) subject = truth.expect(fake_env).that_collection(["d"]) subject.contains_exactly(["e"]) _assert_failure( fake_env, [ "1 missing:\n 0: e", "1 unexpected:\n 0: d", "expected exactly:\n 0: e", "actual values:\n 0: d", ], env = env, msg = "check disjoint values; same length", ) subject = truth.expect(fake_env).that_collection(["f", "g"]) order = subject.contains_exactly(["g", "f"]) _assert_no_failures( fake_env, env = env, msg = "check same elements with expected in different order", ) order.in_order() _assert_failure( fake_env, [ "expected values all found, but with incorrect order", "0: g found at offset 1", "1: f found at offset 0", "actual values:", "0: f", "1: g", ], env = env, msg = "check same elements out of order", ) subject = truth.expect(fake_env).that_collection(["x", "y"]) subject.contains_exactly(["y"]) _assert_failure( fake_env, [ "1 unexpected:\n 0: x", "expected exactly:\n 0: y", "actual values:\n 0: x\n 1: y", ], env = env, msg = "check expected subset of actual", ) subject = truth.expect(fake_env).that_collection(["a", "b", "c", "d"]) subject.contains_exactly(["a", "b", "c", "d"]) _assert_no_failures( fake_env, env = env, msg = "check expected and actual with exact elements and order; 4 values", ) subject.contains_exactly(["d", "b", "a", "c"]) _assert_no_failures( fake_env, env = env, msg = "check expected and actual same elements and different order; 4 values", ) subject = truth.expect(fake_env).that_collection(["a", "b", "a"]) subject.contains_exactly(["a", "b", "a"]) _assert_no_failures( fake_env, env = env, msg = "check multiplicity, same expected/actual order", ) subject.contains_exactly(["b", "a", "a"]) _assert_no_failures( fake_env, env = env, msg = "check multiplicity; different expected/actual order", ) subject = truth.expect(fake_env).that_collection([ "one", "two", "one", "three", "one", "four", ]) subject.contains_exactly(["one", "two", "three", "five"]) _assert_failure( fake_env, [ ("1 missing:\n" + " 0: five"), ("3 unexpected:\n" + " 0: four\n" + " 1: one\n" + " 2: one\n"), ("expected exactly:\n" + " 0: one\n" + " 1: two\n" + " 2: three\n" + " 3: five\n"), ], env = env, msg = "check multiplicity; expected with multiple, expected with unique", ) subject = truth.expect(fake_env).that_collection(["one", "four", "three", "two", "five"]) order = subject.contains_exactly(["one", "two", "three", "four", "five"]) _assert_no_failures( fake_env, env = env, msg = "check same elements with expected in different order", ) order.in_order() _assert_failure( fake_env, [ "expected values all found, but with incorrect order:", "0: one found at offset 0", "1: two found at offset 3", "2: three found at offset 2", "3: four found at offset 1", "4: five found at offset 4", "actual values:", "0: one", "1: four", "2: three", "3: two", "4: five", ], env = env, msg = "check same elements out of order", ) _end(env, fake_env) _suite.append(collection_contains_exactly_test) def collection_contains_exactly_predicates_test(name): analysis_test(name, impl = _collection_contains_exactly_predicates_test, target = "truth_tests_helper") def _collection_contains_exactly_predicates_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection([]) subject.contains_exactly_predicates([matching.contains("a")]) _assert_failure( fake_env, [ "1 missing:\n 0: ", "expected exactly:\n 0: ", "actual values:\n ", ], env = env, msg = "check empty actual vs non-empty expected", ) subject = truth.expect(fake_env).that_collection(["b"]) subject.contains_exactly_predicates([]) _assert_failure( fake_env, [ "1 unexpected:\n 0: b", "expected exactly:\n ", "actual values:\n 0: b", ], env = env, msg = "check non-empty actual vs empty expected", ) subject = truth.expect(fake_env).that_collection(["c"]) order = subject.contains_exactly_predicates([matching.contains("c")]) _assert_no_failures( fake_env, env = env, msg = "check expected and actual with same elements in same order", ) order.in_order() _assert_no_failures( fake_env, env = env, msg = "check exact elements are in order", ) subject = truth.expect(fake_env).that_collection(["d"]) subject.contains_exactly_predicates([matching.contains("e")]) _assert_failure( fake_env, [ "1 missing:\n 0: ", "1 unexpected:\n 0: d", "expected exactly:\n 0: ", "actual values:\n 0: d", ], env = env, msg = "check disjoint values; same length", ) subject = truth.expect(fake_env).that_collection(["f", "g"]) order = subject.contains_exactly_predicates([ matching.contains("g"), matching.contains("f"), ]) _assert_no_failures( fake_env, env = env, msg = "check same elements with expected in different order", ) order.in_order() _assert_failure( fake_env, [ "expected values all found, but with incorrect order", "0: matched at offset 1 (matched: g)", "1: matched at offset 0 (matched: f)", "actual values:", "0: f", "1: g", ], env = env, msg = "check same elements out of order", ) subject = truth.expect(fake_env).that_collection(["x", "y"]) subject.contains_exactly_predicates([matching.contains("y")]) _assert_failure( fake_env, [ "1 unexpected:\n 0: x", "expected exactly:\n 0: ", "actual values:\n 0: x\n 1: y", ], env = env, msg = "check expected subset of actual", ) subject = truth.expect(fake_env).that_collection(["a", "b", "c", "d"]) subject.contains_exactly_predicates([ matching.contains("a"), matching.contains("b"), matching.contains("c"), matching.contains("d"), ]) _assert_no_failures( fake_env, env = env, msg = "check expected and actual with exact elements and order; 4 values", ) subject.contains_exactly_predicates([ matching.contains("d"), matching.contains("b"), matching.contains("a"), matching.contains("c"), ]) _assert_no_failures( fake_env, env = env, msg = "check expected and actual same elements and different order; 4 values", ) subject = truth.expect(fake_env).that_collection(["a", "b", "a"]) subject.contains_exactly_predicates([ matching.contains("a"), matching.contains("b"), matching.contains("a"), ]) _assert_no_failures( fake_env, env = env, msg = "check multiplicity, same expected/actual order", ) subject.contains_exactly_predicates([ matching.contains("b"), matching.contains("a"), matching.contains("a"), ]) _assert_no_failures( fake_env, env = env, msg = "check multiplicity; different expected/actual order", ) subject = truth.expect(fake_env).that_collection([ "one", "two", "one", "three", "one", "four", ]) subject.contains_exactly_predicates([ matching.contains("one"), matching.contains("two"), matching.contains("three"), matching.contains("five"), ]) _assert_failure( fake_env, [ ("1 missing:\n" + " 0: "), ("3 unexpected:\n" + " 0: four\n" + " 1: one\n" + " 2: one\n"), ("expected exactly:\n" + " 0: \n" + " 1: \n" + " 2: \n" + " 3: \n"), ], env = env, msg = "check multiplicity; expected with multiple, expected with unique", ) _end(env, fake_env) _suite.append(collection_contains_exactly_predicates_test) def collection_contains_none_of_test(name): analysis_test(name, impl = _collection_contains_none_of_test, target = "truth_tests_helper") def _collection_contains_none_of_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a"]) subject.contains_none_of(["b"]) _assert_no_failures( fake_env, env = env, msg = "check actual contains none of", ) subject.contains_none_of(["a"]) _assert_failure( fake_env, [ "expected not to contain any of:", " 0: a", "but 1 found", "actual values:", ], env = env, msg = "check actual contains an unexpected value", ) _end(env, fake_env) _suite.append(collection_contains_none_of_test) def collection_not_contains_test(name): analysis_test(name, impl = _collection_not_contains_test, target = "truth_tests_helper") def _collection_not_contains_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a"]) subject.not_contains("b") _assert_no_failures( fake_env, env = env, msg = "check not_contains passes", ) subject.not_contains("a") _assert_failure( fake_env, [ "expected not to contain", "0: a", ], env = env, msg = "check not_contains fails", ) _suite.append(collection_not_contains_test) def collection_not_contains_predicate_test(name): analysis_test(name, impl = _collection_not_contains_predicate_test, target = "truth_tests_helper") def _collection_not_contains_predicate_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a"]) subject.not_contains_predicate(matching.contains("b")) _assert_no_failures( fake_env, env = env, msg = "check actual does not contain a value", ) subject.not_contains_predicate(matching.contains("a")) _assert_failure( fake_env, ["expected not to contain any of: ", "but 1 found:", "0: a"], env = env, msg = "check actual contains an unexpected value", ) _end(env, fake_env) _suite.append(collection_not_contains_predicate_test) def collection_offset_test(name): analysis_test(name, impl = _collection_offset_test, target = "truth_tests_helper") def _collection_offset_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_collection(["a", "b", "c"]) offset_value = subject.offset(0, factory = lambda v, meta: v) ut_asserts.true(env, offset_value == "a", "unexpected offset value at 0") offset_value = subject.offset(-1, factory = lambda v, meta: v) ut_asserts.true(env, offset_value == "c", "unexpected offset value at -1") subject.offset(1, factory = subjects.str).equals("not-b") _assert_failure( fake_env, [".offset(1)"], env = env, msg = "offset error message context not found", ) _end(env, fake_env) _suite.append(collection_offset_test) def _collection_transform_test(name): analysis_test(name, impl = _collection_transform_test_impl, target = "truth_tests_helper") def _collection_transform_test_impl(env, target): _ = target # @unused fake_env = _fake_env(env) starter = truth.expect(fake_env).that_collection(["alan", "bert", "cari"]) actual = starter.transform( "values that contain a", filter = lambda v: "a" in v, ) actual.contains("not-present") _assert_failure( fake_env, [ "transform()", "0: alan", "1: cari", "transform: values that contain a", ], env = env, msg = "transform with lambda filter", ) actual = starter.transform(filter = matching.contains("b")) actual.contains("not-present") _assert_failure( fake_env, [ "0: bert", "transform: filter=", ], env = env, msg = "transform with matcher filter", ) def contains_c(v): return "c" in v actual = starter.transform(filter = contains_c) actual.contains("not-present") _assert_failure( fake_env, [ "0: cari", "transform: filter=contains_c(...)", ], env = env, msg = "transform with named function filter", ) actual = starter.transform( "v.upper(); match even offsets", map_each = lambda v: "{}-{}".format(v[0], v[1].upper()), loop = enumerate, ) actual.contains("not-present") _assert_failure( fake_env, [ "transform()", "0: 0-ALAN", "1: 1-BERT", "2: 2-CARI", "transform: v.upper(); match even offsets", ], env = env, msg = "transform with all args", ) _end(env, fake_env) _suite.append(_collection_transform_test) def execution_info_test(name): analysis_test(name, impl = _execution_info_test, target = "truth_tests_helper") def _execution_info_test(env, target): # TODO(rlevasseur): Remove this after cl/474597236 is released in Blaze exec_info_is_ctor = str(testing.ExecutionInfo) == "" if not exec_info_is_ctor: return fake_env = _fake_env(env) subject = truth.expect(fake_env).that_target(target).provider(testing.ExecutionInfo) subject.requirements().contains_exactly({"EIKEY1": "EIVALUE1"}) _assert_no_failures(fake_env, env = env) if _IS_BAZEL_6_OR_HIGHER: subject.exec_group().equals("THE_EXEC_GROUP") _assert_no_failures(fake_env, env = env) _end(env, fake_env) _suite.append(execution_info_test) def depset_file_subject_test(name): analysis_test(name, impl = _depset_file_subject_test, target = "truth_tests_data_files") def _depset_file_subject_test(env, target): fake_env = _fake_env(env) # We go through a target so that the usual format_str kwargs are present. subject = truth.expect(fake_env).that_target(target).default_outputs() # The CollectionSubject tests cover contains_at_least_predicates in # more depth, so just do some basic tests here. subject.contains_at_least_predicates([ matching.file_path_matches("txt"), ]) _assert_no_failures(fake_env, env = env) subject.contains_at_least_predicates([ matching.file_path_matches("NOT THERE"), ]) _assert_failure( fake_env, ["NOT THERE", "file1.txt"], env = env, ) subject.contains_predicate(matching.file_path_matches("txt")) _assert_no_failures(fake_env, env = env) subject.contains_predicate(matching.file_path_matches("NOT THERE")) _assert_failure( fake_env, ["NOT THERE", "file1.txt"], env = env, ) subject.contains_exactly(["{package}/testdata/file1.txt"]) _assert_no_failures(fake_env, env = env) subject.contains_exactly(["NOT THERE"]) _assert_failure( fake_env, ["NOT THERE", "file1.txt"], env = env, ) subject.not_contains("does-not-contain") _assert_no_failures( fake_env, env = env, msg = "DepsetFilesubject.not_contains success test", ) subject.not_contains("{package}/testdata/file1.txt") _assert_failure( fake_env, ["expected not to contain any of", "file1.txt"], env = env, msg = "DepsetFilesubject.not_contains failure test", ) _end(env, fake_env) _suite.append(depset_file_subject_test) def dict_subject_test(name): analysis_test(name, impl = _dict_subject_test, target = "truth_tests_helper") def _dict_subject_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_dict({"a": 1, "b": 2, "c": 3}) def factory(value, *, meta): return struct(value = value, meta = meta) actual = subject.get("a", factory = factory) truth.expect(env).that_int(actual.value).equals(1) truth.expect(env).that_collection(actual.meta._exprs).contains("get(a)") subject.contains_exactly({"a": 1, "b": 2, "c": 3}) _assert_no_failures(fake_env, env = env) subject.contains_exactly({"d": 4, "a": 99}) _assert_failure( fake_env, [ ("expected dict: {\n" + " a: \n" + " d: \n" + "}\n"), ("1 missing keys:\n" + " 0: d\n"), ("2 unexpected keys:\n" + " 0: b\n" + " 1: c\n"), ("1 incorrect entries:\n" + "key a:\n" + " expected: 99\n" + " but was : 1\n"), ("actual: {\n" + " a: \n" + " b: \n" + " c: \n" + "}\n"), ], env = env, ) subject.contains_at_least({"a": 1}) _assert_no_failures(fake_env, env = env) subject.contains_at_least({"d": 91, "a": 74}) _assert_failure( fake_env, [ ("expected dict: {\n" + " a: \n" + " d: \n" + "}\n"), ("1 missing keys:\n" + " 0: d\n"), ("1 incorrect entries:\n" + "key a:\n" + " expected: 74\n" + " but was : 1\n"), ("actual: {\n" + " a: \n" + " b: \n" + " c: \n" + "}\n"), ], env = env, ) # NOTE: we use the real env for this, since we're doing a real assert truth.expect(env).that_collection( subject.keys().actual, ).contains_exactly(["a", "b", "c"]) _end(env, fake_env) _suite.append(dict_subject_test) def expect_test(name): analysis_test(name, impl = _expect_test, target = "truth_tests_helper") def _expect_test(env, target): fake_env = _fake_env(env) expect = truth.expect(fake_env) ut_asserts.true( env, expect.that_target(target) != None, msg = "expect.that_target", ) _assert_no_failures(fake_env, env = env) expect.where( foo = "bar", baz = "qux", ).that_bool(True).equals(False) _assert_failure( fake_env, ["foo: bar", "baz: qux"], env = env, ) _end(env, fake_env) _suite.append(expect_test) def file_subject_test(name): analysis_test(name, impl = _file_subject_test, target = "truth_tests_data_files") def _file_subject_test(env, target): fake_env = _fake_env(env) package = target.label.package expect = truth.expect(fake_env) subject = expect.that_file(target.files.to_list()[0]) subject.short_path_equals(package + "/testdata/file1.txt") _assert_no_failures(fake_env, env = env) subject.short_path_equals("landon-and-hope-forever.txt") _assert_failure( fake_env, [ "value of: file", "expected: landon-and-hope-forever.txt", "actual: {}/testdata/file1.txt".format(package), ], env = env, ) subject = expect.that_file( target.files.to_list()[0], meta = expect.meta.derive( format_str_kwargs = {"custom": "file1.txt"}, ), ) # NOTE: We purposefully don't use `{package}` because we're just # testing the `{custom}` keyword subject.short_path_equals(package + "/testdata/{custom}") _assert_no_failures(fake_env, env = env) _end(env, fake_env) _suite.append(file_subject_test) def label_subject_test(name): analysis_test(name, impl = _label_subject_test, target = "truth_tests_helper") def _label_subject_test(env, target): fake_env = _fake_env(env) expect = truth.expect(fake_env) subject = expect.that_target(target).label() subject.equals("//tests:truth_tests_helper") _assert_no_failures(fake_env, env = env) subject.equals(Label("//tests:truth_tests_helper")) _assert_no_failures(fake_env, env = env) subject.equals("//nope") _assert_failure( fake_env, ["expected: " + str(Label("//nope")), "actual:", "_helper"], env = env, ) subject = subjects.label(Label("//some/pkg:label"), expect.meta) subject.is_in(["//foo:bar", "//some/pkg:label"]) _assert_no_failures(fake_env, msg = "is_in with matched str values", env = env) subject.is_in([Label("//bar:baz"), Label("//some/pkg:label")]) _assert_no_failures(fake_env, msg = "is_in with matched label values", env = env) subject.is_in(["//not:there", Label("//other:value")]) _assert_failure( fake_env, [ "expected any of:", "//not:there", "//other:value", "actual: " + str(Label("//some/pkg:label")), ], msg = "check is_in fails", env = env, ) _end(env, fake_env) _suite.append(label_subject_test) def runfiles_subject_test(name): analysis_test(name, impl = _runfiles_subject_test, target = "truth_tests_helper") def _runfiles_subject_test(env, target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_target(target).runfiles() subject.contains("{workspace}/{package}/default_runfile1.txt") _assert_no_failures(fake_env, env = env) subject.contains("does-not-exist") _assert_failure( fake_env, [ "expected to contain: does-not-exist", "actual default runfiles:", "default_runfile1.txt", "target: ".format(target.label), ], env = env, msg = "check contains", ) subject.contains_none_of(["{workspace}/{package}/not-there.txt"]) _assert_no_failures(fake_env, env = env) subject.contains_none_of(["{workspace}/{package}/default_runfile1.txt"]) _assert_failure( fake_env, [ "expected not to contain any of", "default_runfile1.txt", env.ctx.workspace_name, ], env = env, msg = "check contains none of", ) subject.contains_exactly([ "{workspace}/{package}/default_runfile1.txt", "{workspace}/{package}/truth_tests_helper.txt", ]) _assert_no_failures(fake_env, env = env) subject.contains_exactly([ "{workspace}/{package}/not-there.txt", ]) _assert_failure( fake_env, [ "1 missing", "not-there.txt", env.ctx.workspace_name, ], env = env, msg = "check contains_exactly fails", ) subject.contains_at_least([ "{workspace}/{package}/default_runfile1.txt", ]) _assert_no_failures(fake_env, env = env) subject.contains_at_least([ "not-there.txt", ]) _assert_failure( fake_env, [ "1 expected paths missing", "not-there.txt", env.ctx.workspace_name, ], env = env, msg = "check contains_at_least fails", ) _end(env, fake_env) _suite.append(runfiles_subject_test) def str_subject_test(name): analysis_test(name, impl = _str_subject_test, target = "truth_tests_helper") def _str_subject_test(env, _target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_str("foobar") subject.contains("ob") _assert_no_failures(fake_env, env = env) subject.contains("nope") _assert_failure( fake_env, ["expected to contain: nope", "actual: foobar"], env = env, msg = "check contains", ) subject.equals("foobar") _assert_no_failures(fake_env, env = env) subject.equals("not foobar") _assert_failure( fake_env, ["expected: not foobar", "actual: foobar"], env = env, msg = "check equals", ) result = subject.split("b") ut_asserts.true(env, result.actual == ["foo", "ar"], "incorrectly split") subject.not_equals("foobar") _assert_failure( fake_env, ["expected not to be: foobar", "actual: foobar"], env = env, msg = "check not_equals with equal value", ) subject.not_equals(47) _assert_failure( fake_env, ["expected not to be: 47 (type: int)", "actual: foobar (type: string)"], env = env, msg = "check not_equals with different type", ) subject.not_equals("not-foobar") _assert_no_failures( fake_env, env = env, msg = "check not_equals with unequal value of same type", ) subject.is_in(["xxx", "yyy", "zzz"]) _assert_failure( fake_env, ["expected any of:", "xxx", "yyy", "zzz", "actual: foobar"], env = env, msg = "check is_in with non-matching values", ) subject.is_in(["foobar", "y", "z"]) _assert_no_failures( fake_env, env = env, msg = "check is_in with matching values", ) _end(env, fake_env) _suite.append(str_subject_test) def target_subject_test(name): analysis_test(name, impl = _target_subject_test, target = "truth_tests_helper") #TODO also file target def _target_subject_test(env, target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_target(target) # First a static string, no formatting parameters result = subject.action_generating("{package}/default_runfile1.txt") ut_asserts.true(env, result != None, msg = "action_generating gave None") # Now try it with formatting parameters result = subject.action_generating("{package}/{name}.txt") ut_asserts.true(env, result != None, msg = "action_generating gave None") result = subject.label() ut_asserts.true(env, result != None, msg = "label gave None") subject = truth.expect(fake_env).that_target(target) tags = subject.tags() ut_asserts.true(env, tags != None, msg = "tags gave None") tags.contains_exactly(["tag1", "tag2"]) _assert_no_failures( fake_env, env = env, msg = "check TargetSubject.tags()", ) attr_subject = subject.attr("testonly") ut_asserts.true(env, attr_subject != None, msg = "attr(testonly) gave None") custom_subject = subject.attr( "testonly", factory = lambda v, meta: struct(custom = True), ) ut_asserts.true( env, custom_subject.custom == True, msg = "attr() with custom factory gave wrong value", ) output_group_subject = subject.output_group("some_group") output_group_subject.contains("{package}/output_group_file.txt") _assert_no_failures( fake_env, env = env, msg = "check TargetSubject.output_group()", ) _end(env, fake_env) _suite.append(target_subject_test) def run_environment_info_subject_test(name): analysis_test(name, impl = _run_environment_info_subject_test, target = "truth_tests_helper") def _run_environment_info_subject_test(env, target): fake_env = _fake_env(env) subject = truth.expect(fake_env).that_target(target).provider( RunEnvironmentInfo, ) subject.environment().contains_exactly({ "EKEY1": "EVALUE1", "EKEY2": "EVALUE2", }) _assert_no_failures(fake_env, env = env) subject.inherited_environment().contains_exactly(["INHERIT1", "INHERIT2"]) _assert_no_failures(fake_env, env = env) _end(env, fake_env) _suite.append(run_environment_info_subject_test) def _add_failure_works_test(name): analysis_test( name, impl = _add_failure_works_test_impl, target = "truth_tests_noop", expect_failure = True, ) def _add_failure_works_test_impl(env, target): """Test that the real add_failure() codepath works. All the other tests mock out the fail() call, this is the one test that doesn't. """ _ = target # @unused # NOTE: this prints a spurious message. env.expect.meta.add_failure("FAKE PROBLEM", "FAKE ACTUAL") failures = list(env._failures) env._failures.clear() if len(failures) != 1: env.fail("Expected len(failures) == 1, got " + str(len(failures))) else: failure = failures[0] if "FAKE PROBLEM" not in failure: env.fail("Expected string 'FAKE PROBLEM' not found in failure message") if "FAKE ACTUAL" not in failure: env.fail("Expected string 'FAKE ACTUAL' not found in failure message") _suite.append(_add_failure_works_test) def _assert_no_failures(fake_env, *, env, msg = ""): fail_lines = [ "expected no failures, but found failures", msg, "===== FAILURE MESSAGES =====", ] fail_lines.extend(fake_env.failures) fail_lines.append("===== END FAILURE MESSAGES ====") fail_msg = "\n".join(fail_lines) ut_asserts.true(env, len(fake_env.failures) == 0, msg = fail_msg) fake_env.reset() def _assert_failure(fake_env, expected_strs, *, env, msg = ""): if len(fake_env.failures) != 1: env.fail("expected exactly 1 failure, but found {}".format(len(fake_env.failures))) if len(fake_env.failures) > 0: failure = fake_env.failures[0] for expected in expected_strs: if expected not in failure: env.fail(( "\nFailure message incorrect:\n{}\n" + "===== EXPECTED ERROR SUBSTRING =====\n{}\n" + "===== END EXPECTED ERROR SUBSTRING =====\n" + "===== ACTUAL FAILURE MESSAGE =====\n{}\n" + "===== END ACTUAL FAILURE MESSAGE =====" ).format( msg, expected, failure, )) fake_env.reset() def _test_helper_impl(ctx): action_output = ctx.actions.declare_file("action.txt") ctx.actions.run( outputs = [action_output], executable = ctx.executable.tool, arguments = [ "arg1", "--boolflag", "--arg1flag", "arg1value", "--arg2flag=arg2value", ], mnemonic = "Action1", ) if _IS_BAZEL_6_OR_HIGHER: exec_info_bazel_6_kwargs = {"exec_group": "THE_EXEC_GROUP"} else: exec_info_bazel_6_kwargs = {} return [ DefaultInfo( default_runfiles = ctx.runfiles( files = [ _empty_file(ctx, "default_runfile1.txt"), _empty_file(ctx, ctx.label.name + ".txt"), ], ), ), testing.TestEnvironment( environment = {"EKEY1": "EVALUE1", "EKEY2": "EVALUE2"}, inherited_environment = ["INHERIT1", "INHERIT2"], ), testing.ExecutionInfo({"EIKEY1": "EIVALUE1"}, **exec_info_bazel_6_kwargs), OutputGroupInfo( some_group = depset([_empty_file(ctx, "output_group_file.txt")]), ), ] test_helper = rule( implementation = _test_helper_impl, attrs = { "tool": attr.label( default = ":truth_tests_noop", executable = True, cfg = "exec", ), }, ) def _empty_file(ctx, name): file = ctx.actions.declare_file(name) ctx.actions.write(file, content = "") return file def _noop_binary_impl(ctx): return DefaultInfo(executable = _empty_file(ctx, ctx.label.name)) noop_binary = rule( implementation = _noop_binary_impl, executable = True, ) def truth_test_suite(name): # Unit tests can't directly create File objects, so we have a generic # collection of files they can put in custom attributes to use. native.filegroup( name = "truth_tests_data_files", srcs = native.glob(["testdata/**"]), ) test_helper( name = "truth_tests_helper", tags = ["tag1", "tag2"], ) noop_binary(name = "truth_tests_noop") test_suite( name = name, tests = _suite, )