# python3 # Copyright 2021 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. from datetime import datetime import io import re import tempfile import toml import unittest from lib import gen from lib import cargo from lib import compiler # An example output from `cargo tree` # 1. We patch paths into the Cargo.toml files that we feed to `cargo tree` and # this demonstrates that for the "quote" crate by including the path there. # 2. We include features in the output, demonstrated by many deps here. And we # also have an example of a crate without any features with the second # occurrence of "unicode-xid". # 3. Some crates get de-duped and that means get a (*) on them, which we can # see. # 4. A proc-macro crate is a different kind of crate and is designated by the # (proc-macro) after the version, and we have an example of that with # cxxbridge-macro. # 5. We include direct deps as well as transitive deps, which have more than one # piece of ascii art on their line. CARGO_TREE = """ cxx v1.0.56 (/path/to/chromium/src/third_party/rust/cxx/v1/crate) ├── cxxbridge-macro v1.0.56 (proc-macro) │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations │ │ └── unicode-xid v0.2.2 default │ ├── quote v1.0.10 (/path/to/chromium/src/third_party/rust/quote/v1/crate) default,proc-macro │ │ └── proc-macro2 v1.0.32 default,proc-macro,span-locations (*) │ └── syn v1.0.81 clone-impls,default,derive,full,parsing,printing,proc-macro,quote │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations (*) │ ├── quote v1.0.10 default,proc-macro (*) │ └── unicode-xid v0.2.2 └── link-cplusplus v1.0.5 default """.lstrip("\n") class GenTestCase(unittest.TestCase): def assertListSortedEqual(self, a, b, msg=None): a.sort() b.sort() if msg: self.assertListEqual(a, b, msg=msg) else: self.assertListEqual(a, b) def matching_archs(self, matching: str) -> set[str]: return { arch for arch in compiler._RUSTC_ARCH_TO_BUILD_CONDITION if re.search(matching, arch) } def all_archs(self): return set(compiler._RUSTC_ARCH_TO_BUILD_CONDITION.keys()) def make_args(self): class Args: pass args = Args args.verbose = False return args def test_parse_cargo_tree_dependency_line(self): args = self.make_args() lines = CARGO_TREE.split("\n") # Here we are simulating `cargo tree` on a third-party Cargo.toml file, # not our special third_party.toml file. So we pass False as # is_third_party_toml, and we can give an empty parsed toml file as it # won't be used then. # cxx v1.0.56 (/path/to/chromium/src/third_party/rust/cxx/v1/crate) r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[0]) self.assertEqual(r, None) # not a dependency # ├── cxxbridge-macro v1.0.56 (proc-macro) r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[1]) expected = gen.CargoTreeDependency(cargo.CrateKey( "cxxbridge-macro", "1.0.56"), full_version="1.0.56") self.assertEqual(r, expected) # │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[2]) expected = gen.CargoTreeDependency( cargo.CrateKey("proc-macro2", "1.0.32"), full_version="1.0.32", features=["default", "proc-macro", "span-locations"]) self.assertEqual(r, expected) # │ │ └── unicode-xid v0.2.2 default r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[3]) expected = gen.CargoTreeDependency(cargo.CrateKey( "unicode-xid", "0.2.2"), full_version="0.2.2", features=["default"]) self.assertEqual(r, expected) # │ ├── quote v1.0.10 (/path/to/chromium/src/third_party/rust/quote/v1/crate) default,proc-macro r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[4]) expected = gen.CargoTreeDependency( cargo.CrateKey("quote", "1.0.10"), full_version="1.0.10", crate_path="/path/to/chromium/src/third_party/rust/quote/v1/crate", features=["default", "proc-macro"]) self.assertEqual(r, expected) # │ │ └── proc-macro2 v1.0.32 default,proc-macro,span-locations (*) r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[5]) expected = gen.CargoTreeDependency( cargo.CrateKey("proc-macro2", "1.0.32"), full_version="1.0.32", features=["default", "proc-macro", "span-locations"]) self.assertEqual(r, expected) # │ └── syn v1.0.81 clone-impls,default,derive,full,parsing,printing,proc-macro,quote r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[6]) expected = gen.CargoTreeDependency(cargo.CrateKey("syn", "1.0.81"), full_version="1.0.81", features=[ "clone-impls", "default", "derive", "full", "parsing", "printing", "proc-macro", "quote" ]) self.assertEqual(r, expected) # │ ├── proc-macro2 v1.0.32 default,proc-macro,span-locations (*) r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[7]) expected = gen.CargoTreeDependency( cargo.CrateKey("proc-macro2", "1.0.32"), full_version="1.0.32", features=["default", "proc-macro", "span-locations"]) self.assertEqual(r, expected) # │ ├── quote v1.0.10 default,proc-macro (*) r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[8]) expected = gen.CargoTreeDependency(cargo.CrateKey("quote", "1.0.10"), full_version="1.0.10", features=["default", "proc-macro"]) self.assertEqual(r, expected) # │ └── unicode-xid v0.2.2 r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[9]) expected = gen.CargoTreeDependency(cargo.CrateKey( "unicode-xid", "0.2.2"), full_version="0.2.2") self.assertEqual(r, expected) # └── link-cplusplus v1.0.5 default r = gen._parse_cargo_tree_dependency_line(args, dict(), False, lines[10]) d = gen.CargoTreeDependency(cargo.CrateKey("link-cplusplus", "1.0.5"), full_version="1.0.5", features=["default"]) self.assertEqual(r, d) def test_third_party_toml(self): args = self.make_args() THIRD_PARTY_TOML = """ # This dependency has no extensions [dependencies] cxxbridge-macro = "1" # This dependency has an extension, and is default-visible to first-party code. [dependencies.link-cplusplus] build-script-outputs = [ "src/link.rs", "src/cplusplus.rs" ] # This dependency is not visible to first-party code thanks to the extension. [dependencies.syn] allow-first-party-usage = false """ # Here we are simulating parsing our special third_party.toml file, so # we need to present the contents of that file to the # _parse_cargo_tree_dependency_line() function, in order for it to look # for extensions. toml_content = toml.loads(THIRD_PARTY_TOML) line = "├── cxxbridge-macro v1.0.56 (proc-macro)" r = gen._parse_cargo_tree_dependency_line(args, toml_content, True, line) d = gen.CargoTreeDependency( cargo.CrateKey("cxxbridge-macro", "1.0.56"), full_version="1.0.56", # Deps from third_party.toml are visible to first-party code by # default. is_for_first_party_code=True) self.assertEqual(r, d) line = "├── link-cplusplus v1.0.5 default" r = gen._parse_cargo_tree_dependency_line(args, toml_content, True, line) d = gen.CargoTreeDependency( cargo.CrateKey("link-cplusplus", "1.0.5"), full_version="1.0.5", features=["default"], is_for_first_party_code=True, # link-cplusplus has build script outputs listed in our # third_party.toml. build_script_outputs={"src/link.rs", "src/cplusplus.rs"}) self.assertEqual(r, d) line = "└── syn v1.0.81 clone-impls,default" r = gen._parse_cargo_tree_dependency_line(args, toml_content, True, line) d = gen.CargoTreeDependency( cargo.CrateKey("syn", "1.0.81"), full_version="1.0.81", features=["clone-impls", "default"], # Deps from third_party.toml are visible to first-party code unless # they opt out explicitly, which our syn dependency has done. is_for_first_party_code=False) self.assertEqual(r, d) def test_third_party_toml_dev_deps(self): args = self.make_args() THIRD_PARTY_TOML = """ [dependencies] # Nothing. We're testing dev-dependencies here. [dev-dependencies] cxxbridge-macro = "1" # This dependency has an extension, and is default-visible to first-party code. [dev-dependencies.link-cplusplus] build-script-outputs = [ "src/link.rs", "src/cplusplus.rs" ] # This dependency is not visible to first-party code thanks to the extension. [dev-dependencies.syn] allow-first-party-usage = false """ # Here we are simulating parsing our special third_party.toml file, so # we need to present the contents of that file to the # _parse_cargo_tree_dependency_line() function, in order for it to look # for extensions. toml_content = toml.loads(THIRD_PARTY_TOML) line = "├── cxxbridge-macro v1.0.56 (proc-macro)" r = gen._parse_cargo_tree_dependency_line(args, toml_content, True, line) d = gen.CargoTreeDependency( cargo.CrateKey("cxxbridge-macro", "1.0.56"), full_version="1.0.56", # Deps from third_party.toml are visible to first-party code by # default. is_for_first_party_code=True) self.assertEqual(r, d) line = "├── link-cplusplus v1.0.5 default" r = gen._parse_cargo_tree_dependency_line(args, toml_content, True, line) d = gen.CargoTreeDependency( cargo.CrateKey("link-cplusplus", "1.0.5"), full_version="1.0.5", features=["default"], is_for_first_party_code=True, # link-cplusplus has build script outputs listed in our # third_party.toml. build_script_outputs={"src/link.rs", "src/cplusplus.rs"}) self.assertEqual(r, d) line = "└── syn v1.0.81 clone-impls,default" r = gen._parse_cargo_tree_dependency_line(args, toml_content, True, line) d = gen.CargoTreeDependency( cargo.CrateKey("syn", "1.0.81"), full_version="1.0.81", features=["clone-impls", "default"], # Deps from third_party.toml are visible to first-party code unless # they opt out explicitly, which our syn dependency has done. is_for_first_party_code=False) self.assertEqual(r, d) def test_get_archs_third_party_toml(self): crate_key = None # For third_party.toml there's none. usage_data = None # For third_party.toml there's none. # Test what happens when there is a `target` on a dependency. toml_content = toml.loads(""" [dependencies] all-platform-crate = "1" [target."cfg(windows)".dependencies] windows-only-crate = "2" """) # Test case without a target specified on the command line. Since # there's differences per-architecture in the TOML, we will have to test # all architectures. arch_specific = gen._get_archs_of_interest(toml_content, usage_data, None) # Not true as there's a arch-specific dependency. self.assertFalse(arch_specific.is_single_arch()) self.assertSetEqual(self.all_archs(), arch_specific.archs_to_test()) # Test case with a target specified on the command line that matches # the target. We only need to test the target the user specified. arch_specific = gen._get_archs_of_interest(toml_content, usage_data, "x86_64-pc-windows-msvc") # We only ever care about one arch, so everything is single-arch. self.assertTrue(arch_specific.is_single_arch()) self.assertSetEqual({"x86_64-pc-windows-msvc"}, arch_specific.archs_to_test()) # Test case with a target specified on the command line that does not # match the target. We only need to test the target the user specified, # and we're not smart enough to prune it yet.. arch_specific = gen._get_archs_of_interest(toml_content, usage_data, "x86_64-unknown-linux-gnu") # We only ever care about one arch, so everything is single-arch. self.assertTrue(arch_specific.is_single_arch()) self.assertSetEqual({"x86_64-unknown-linux-gnu"}, arch_specific.archs_to_test()) # Test what happens when there is no `target` on a dependency. toml_content = toml.loads(""" [dependencies] all-platform-crate = "1" """) # Test case without a target specified on the command line. arch_specific = gen._get_archs_of_interest(toml_content, usage_data, None) # Is true since we can apply a single architecture's result to other # ones. self.assertTrue(arch_specific.is_single_arch()) # We only need to test one architecture and copy its results to the # rest. self.assertEqual(len(arch_specific.archs_to_test()), 1) # We don't care which architecture will be tested, but in the next test # case we do, so make sure the arbitrary architecture here isn't doesn't # unluckily collide with our next test. self.assertFalse({"x86_64-pc-windows-msvc" }.issubset(arch_specific.archs_to_test())) # Test case with a target specified on the command line. arch_specific = gen._get_archs_of_interest(toml_content, usage_data, "x86_64-pc-windows-msvc") self.assertTrue(arch_specific.is_single_arch()) # We should test the architecture specified. self.assertSetEqual({"x86_64-pc-windows-msvc"}, arch_specific.archs_to_test()) # Ensure we are OK with a [target] section with no dependencies toml_content = toml.loads(""" [dependencies] all-platform-crate = "1" [target."cfg(windows)"] """) arch_specific = gen._get_archs_of_interest(toml_content, usage_data, None) self.assertTrue(arch_specific.is_single_arch()) def make_fake_parent(self, child_name: str, archset_where_parent_used: compiler.ArchSet, parent_is_arch_specific: bool, parent_has_arch_specific_deps: bool ) -> gen.ArchSpecific: if not parent_has_arch_specific_deps: toml_content = toml.loads(""" [dependencies] {} = "1" """.format(child_name)) else: toml_content = toml.loads(""" [target."cfg(windows)".dependencies] {} = "1" """.format(child_name)) parent_crate_data = gen.PerCrateData() parent_crate_data.arch_specific = parent_is_arch_specific parent_crate_data.used_on_archs = archset_where_parent_used return gen._get_archs_of_interest(toml_content, parent_crate_data, None) def make_fake_dep(self, dep_key: cargo.CrateKey, parent_requested_features_of_dep: list[str], dep_for_first_party_code: bool, dep_build_script_outputs: set[str]): return gen.CargoTreeDependency( dep_key, features=parent_requested_features_of_dep, is_for_first_party_code=dep_for_first_party_code, build_script_outputs=dep_build_script_outputs) def add_fake_dependency_edges(self, build_data: gen.BuildData, new_keys: set[cargo.CrateKey], **kwargs): parent_name: str = kwargs["parent_name"] parent_arch_specific = self.make_fake_parent( kwargs["dep_name"], kwargs["archset_where_parent_used"], kwargs["parent_is_arch_specific"], kwargs["parent_has_arch_specific_deps"]) dep_key = cargo.CrateKey(kwargs["dep_name"], "1.2.3") dep = self.make_fake_dep(dep_key, kwargs["parent_requested_features_of_dep"], kwargs["dep_for_first_party_code"], kwargs["dep_build_script_outputs"]) parent_crate_key = cargo.CrateKey(parent_name, "1.2.3") if parent_name else None gen._add_edges_for_dep_on_target_arch(build_data, parent_crate_key, parent_arch_specific, kwargs["parent_usage"], kwargs["parent_output"], dep, kwargs["computing_arch"], new_keys) return dep_key def test_add_edges(self): build_data = gen.BuildData() new_keys = set() for_first_party_code = True build_script_outputs = {"src/child-outs.rs"} dep_key = self.add_fake_dependency_edges( build_data, new_keys, parent_name=None, # No parent crate from third_party.toml. # Parent used everywhere. archset_where_parent_used=compiler.ArchSet.ALL(), parent_is_arch_specific=False, parent_has_arch_specific_deps=False, # computing_arch is chosen arbitrarily since the result will apply # to all as parent_has_arch_specific_deps is False computing_arch="aarch64-apple-darwin", # Parent used for a binary. parent_usage=cargo.CrateUsage.FOR_NORMAL, # Adding a build-dependency. parent_output=cargo.CrateBuildOutput.BUILDRS, dep_name="child-name", parent_requested_features_of_dep=["feature1", "feature2"], dep_for_first_party_code=for_first_party_code, dep_build_script_outputs=build_script_outputs) # The new crate was added to new_keys self.assertSetEqual(new_keys, {dep_key}) # The child crate should be in the BuildData self.assertSetEqual(build_data.for_buildrs.all_crates(), {dep_key}) self.assertSetEqual(build_data.for_normal.all_crates(), {dep_key}) self.assertSetEqual(build_data.for_tests.all_crates(), {dep_key}) # In buildrs usage, (since the parent was building BUILDRS output), # the dependency will be used and have data populated. with build_data.for_buildrs.per_crate(dep_key) as crate: # The requested features will be used on _all_ architectures, even # since the ArchSpecific tests one but applies to all. features = crate.features.all_archsets() self.assertListSortedEqual(features, [ ("feature1", compiler.ArchSet.ALL()), ("feature2", compiler.ArchSet.ALL()), ]) # Similarly, the crate is used everywhere. This should always be a # superset of the architectures where features are enabled. self.assertEqual(crate.used_on_archs, compiler.ArchSet.ALL()) # Since the ArchSpecific said the parent is not arch-specific, and # there's no arch-specific deps, the dependency will not be arch- # specific. self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) # There's no edges from the new dependency since we haven't added # those. for o in cargo.CrateBuildOutput: self.assertSetEqual(crate.deps[o].all_deps(), set(), msg="For output type {}".format(o)) # Other usages are not used, and have no features enabled, since the # parent crate is only using this dependency for tests so far, as those # are the only edges we added. with build_data.for_normal.per_crate(dep_key) as crate: self.assertListSortedEqual(crate.features.all_archsets(), []) self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY()) self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) with build_data.for_tests.per_crate(dep_key) as crate: self.assertListSortedEqual(crate.features.all_archsets(), []) self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY()) self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) # The parent would have edges to the child but this was a dep from the # third_party.toml, so there's no parent. # Now add a transitive dependency, which will have a parent: the child # that we added above. We will only add edges to the grand child on # Windows. parent_key = dep_key for_first_party_code = False build_script_outputs = {"src/grand-child-outs.rs"} dep_key = self.add_fake_dependency_edges( build_data, new_keys, parent_name="child-name", # The parent crate of the grand-child. # Parent used everywhere. archset_where_parent_used=compiler.ArchSet.ALL(), parent_is_arch_specific=False, parent_has_arch_specific_deps=True, # Act as if cargo-tree sees the grand-child on Windows, so we're # adding an edge there. Since parent_has_arch_specific_deps is True, # the edge won't be added to other architectures automatically. computing_arch="x86_64-pc-windows-msvc", # Parent was used for buildrs. parent_usage=cargo.CrateUsage.FOR_BUILDRS, # Adding a build-dependency. parent_output=cargo.CrateBuildOutput.BUILDRS, dep_name="grand-child-name", parent_requested_features_of_dep=["featureA", "featureB"], dep_for_first_party_code=False, dep_build_script_outputs=build_script_outputs) current_archset = compiler.ArchSet(initial={"x86_64-pc-windows-msvc"}) # The new crate was added to new_keys self.assertSetEqual(new_keys, {parent_key, dep_key}) # The grand-child crate should be in the BuildData self.assertSetEqual(build_data.for_buildrs.all_crates(), {parent_key, dep_key}) self.assertSetEqual(build_data.for_normal.all_crates(), {parent_key, dep_key}) self.assertSetEqual(build_data.for_tests.all_crates(), {parent_key, dep_key}) # In buildrs usage, (since its parent was building BUILDRS output), # the dependency will be used and have data populated. with build_data.for_buildrs.per_crate(dep_key) as crate: # The requested features will be used on the architecture we're # processing, since they can't be generalized to all architectures. features = crate.features.all_archsets() self.assertListSortedEqual(features, [("featureA", current_archset), ("featureB", current_archset)]) # Similarly, the crate is used on the architecture we're processing. self.assertEqual(crate.used_on_archs, current_archset) # Since the dep is in a target rule (parent_has_arch_specific_deps), # the crate is considered arch-specific. self.assertTrue(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) # There's no edges from the new dependency since we haven't added # those. for o in cargo.CrateBuildOutput: self.assertSetEqual(crate.deps[o].all_deps(), set(), msg="For output type {}".format(o)) # Other usages are not used, and have no features enabled, since the # parent crate is only using this dependency for tests so far, as those # are the only edges we added. with build_data.for_normal.per_crate(dep_key) as crate: self.assertListSortedEqual(crate.features.all_archsets(), []) self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY()) self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) with build_data.for_tests.per_crate(dep_key) as crate: self.assertListSortedEqual(crate.features.all_archsets(), []) self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY()) self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) # The child crate now depends on the grand-child crate on the computed. # architecture, only for it's buildrs script. with build_data.for_buildrs.per_crate(parent_key) as crate: self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.BUILDRS].all_deps(), {dep_key}) self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.NORMAL].all_deps(), set()) self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.TESTS].all_deps(), set()) # We haven't considered the parent crate being built into a crate's # lib/bin as a normal dependency, or tests as a dev-dependency so # there's no edges. with build_data.for_normal.per_crate(parent_key) as crate: for o in cargo.CrateBuildOutput: self.assertSetEqual(crate.deps[o].all_deps(), set(), msg="For output type {}".format(o)) with build_data.for_tests.per_crate(parent_key) as crate: for o in cargo.CrateBuildOutput: self.assertSetEqual(crate.deps[o].all_deps(), set(), msg="For output type {}".format(o)) new_keys = set() # Now we again compute edges between the child and grand-child, for # another architecture, and for the child's normal build output. It # can use different features of the grand-child there. dep_key = self.add_fake_dependency_edges( build_data, new_keys, parent_name="child-name", # The parent crate of the grand-child. # Parent used everywhere. archset_where_parent_used=compiler.ArchSet.ALL(), parent_is_arch_specific=False, parent_has_arch_specific_deps=True, # Act as if cargo-tree sees the grand-child on Fuchsia, so we're # adding an edge there. Since parent_has_arch_specific_deps is True, # the edge won't be added to other architectures automatically. computing_arch="x86_64-fuchsia", # Parent was used for a lib/bin. parent_usage=cargo.CrateUsage.FOR_NORMAL, # Adding a build-dependency. parent_output=cargo.CrateBuildOutput.BUILDRS, dep_name="grand-child-name", parent_requested_features_of_dep=["featureB", "featureC"], dep_for_first_party_code=False, dep_build_script_outputs=build_script_outputs) current_archset = compiler.ArchSet(initial={"x86_64-fuchsia"}) before_archset = compiler.ArchSet(initial={"x86_64-pc-windows-msvc"}) union_archset = compiler.ArchSet( initial={"x86_64-pc-windows-msvc", "x86_64-fuchsia"}) # The grand-child is being used on a new platform, and is arch-specific, # so it will need to compute its deps again. self.assertSetEqual(new_keys, {dep_key}) # In buildrs usage, (since its parent was building BUILDRS output), # the dependency will be used and have data populated. with build_data.for_buildrs.per_crate(dep_key) as crate: # The requested features will be used on the architecture we're # processing, since they can't be generalized to all architectures. features = crate.features.all_archsets() self.assertListSortedEqual(features, [ ("featureA", before_archset), ("featureB", union_archset), ("featureC", current_archset), ]) # The crate is used on the union architectures. self.assertEqual(crate.used_on_archs, union_archset) # There's no edges from the new dependency since we haven't added # those. for o in cargo.CrateBuildOutput: self.assertSetEqual(crate.deps[o].all_deps(), set(), msg="For output type {}".format(o)) # Other usages are not used, and have no features enabled, since the # parent crate is only using this dependency for tests so far, as those # are the only edges we added. with build_data.for_normal.per_crate(dep_key) as crate: self.assertListSortedEqual(crate.features.all_archsets(), []) self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY()) self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) with build_data.for_tests.per_crate(dep_key) as crate: self.assertListSortedEqual(crate.features.all_archsets(), []) self.assertEqual(crate.used_on_archs, compiler.ArchSet.EMPTY()) self.assertFalse(crate.arch_specific) self.assertEqual(crate.for_first_party, for_first_party_code) self.assertEqual(crate.build_script_outputs, build_script_outputs) # The child crate still depends on the grand-child crate for its buildrs # script when building built as a build-dependency. with build_data.for_buildrs.per_crate(parent_key) as crate: self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.BUILDRS].all_deps(), {dep_key}) self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.NORMAL].all_deps(), set()) self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.TESTS].all_deps(), set()) # The child crate now _also_ depends on the grand-child crate for its # buildrs script when building built as a normal dependency. with build_data.for_normal.per_crate(parent_key) as crate: self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.BUILDRS].all_deps(), {dep_key}) self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.NORMAL].all_deps(), set()) self.assertSetEqual( crate.deps[cargo.CrateBuildOutput.TESTS].all_deps(), set()) # We haven't considered the parent crate being built into a crate's # tests as a dev-dependency so there's no edges. with build_data.for_tests.per_crate(parent_key) as crate: for o in cargo.CrateBuildOutput: self.assertSetEqual(crate.deps[o].all_deps(), set(), msg="For output type {}".format(o)) def test_copyright_year(self): modern = b"# Copyright 2001 The Chromium Authors" with tempfile.NamedTemporaryFile() as f: f.write(modern) f.flush() self.assertEqual(gen._get_copyright_year(f.name), "2001") ancient = b"# Copyright (c) 2001 The Chromium Authors. All rights reserved." with tempfile.NamedTemporaryFile() as f: f.write(ancient) f.flush() self.assertEqual(gen._get_copyright_year(f.name), "2001") self.assertEqual(gen._get_copyright_year("/file/does/not/exist"), str(datetime.now().year))