def test_sort_order(self): """ Verifies that sorting Dependency instances produces the desired ordering: alphanumeric, monorepo artifacts first. """ dep1 = dependency.new_dep_from_maven_art_str( "com.google.guava:guava:20.0", "name") dep2 = dependency.new_dep_from_maven_art_str( "com.google.guava:zoouava:20.0", "name") art_def = buildpom.maven_artifact("com.zoogle.guava", "art1", "1.0") art_def = buildpom._augment_art_def_values(art_def, None, "pack1", None, None, pomgenmode.DYNAMIC) dep3 = dependency.new_dep_from_maven_artifact_def(art_def, None) art_def = buildpom.maven_artifact("com.google.guava", "art1", "1.0") art_def = buildpom._augment_art_def_values(art_def, None, "pack1", None, None, pomgenmode.DYNAMIC) dep4 = dependency.new_dep_from_maven_artifact_def(art_def, None) l = [dep3, dep2, dep1, dep4] l.sort() self.assertIs(dep4, l[0]) self.assertIs(dep3, l[1]) self.assertIs(dep1, l[2]) self.assertIs(dep2, l[3])
def _parse_conflict_resolution(json_dep_tree, mvn_install_name): conflict_resolution = {} if "conflict_resolution" in json_dep_tree: # if there is a conflict_resolution attribute, we have to honor it # it maps actual gav -> gav used in rest of file, with the only diff # being the version. # # for example: # "conflict_resolution": { # "com.sun.jersey:jersey-client:1.17-ext": "com.sun.jersey:jersey-client:1.17", # "com.sun.jersey:jersey-core:1.17-ext": "com.sun.jersey:jersey-core:1.17" # }, # above, the dep version we want is "1.17-ext", but rest of the pinned # file uses "1.17" # # we'll store this as a lookup the other way: # dep used in pinned file -> dep we actually want for gav_key, gav_value in json_dep_tree["conflict_resolution"].items(): wanted_dep = dependency.new_dep_from_maven_art_str( gav_key, mvn_install_name) actual_dep = dependency.new_dep_from_maven_art_str( gav_value, mvn_install_name) assert actual_dep not in conflict_resolution conflict_resolution[actual_dep] = wanted_dep return conflict_resolution
def test_conflict_resolution_is_honored(self): """ Verifies that the pinned file's "conflict_resolution" attribute is handled. """ fd, path = tempfile.mkstemp() with os.fdopen(fd, 'w') as f: f.write(MVN_INSTALL_JSON_CONTENT_CONFLICT_RESOLUTION) result = bazel.parse_maven_install("maven", path) self.assertEqual(2, len(result)) dep, transitives, exclusions = result[0] self.assertEqual( dependency.new_dep_from_maven_art_str( "ch.qos.logback:logback-classic:1.2.3", "maven"), dep) self.assertEqual(1, len(transitives)) transitive_guava = transitives[0] # we expect to get the dep from conflict_resolution map self.assertEqual( dependency.new_dep_from_maven_art_str( "com.google.guava:guava:31.0.1-jre", "maven"), transitive_guava) guava, transitives, exclusions = result[1] # we expect to get the dep from conflict_resolution map self.assertEqual( dependency.new_dep_from_maven_art_str( "com.google.guava:guava:31.0.1-jre", "maven"), guava) self.assertEqual(transitive_guava, guava)
def test_sort_order_includes_scope__all_deps_with_scope__no_classifier( self): """ Ensures that the optional scope of an external dependency is included when sorting dependencies. """ dep1 = dependency.new_dep_from_maven_art_str( "com.grail.services:arthur-common-thrift-api:jar:2.2.17", "name1") dep1.scope = "s1" dep2 = dependency.new_dep_from_maven_art_str( "com.grail.services:arthur-common-thrift-api:jar:2.2.17", "name2") dep2.scope = "s3" dep3 = dependency.new_dep_from_maven_art_str( "com.grail.services:arthur-common-thrift-api:jar:2.2.17", "name3") dep3.scope = "s2" l = [ dep1, dep2, dep3, ] l.sort() self.assertIs(dep1, l[0]) self.assertIs(dep3, l[1]) self.assertIs(dep2, l[2])
def test_equals_ignores_version(self): dep1 = dependency.new_dep_from_maven_art_str( "com.google.guava:guava:20.0", "name") dep2 = dependency.new_dep_from_maven_art_str( "com.google.guava:guava:100", "name") self.assertEqual(dep1, dep2)
def test_depman_pom__sanity(self): """ Ensures that dependency management pom generation isn't totally broken. """ ws = workspace.Workspace("some/path", [], exclusions.src_exclusions(), maveninstallinfo.NOOP, pomcontent.NOOP) artifact_def = buildpom.MavenArtifactDef( "g1", "a2", "1.2.3", gen_dependency_management_pom=True) dep = dependency.new_dep_from_maven_artifact_def(artifact_def) pomgen = pom.DependencyManagementPomGen(ws, artifact_def, dep, TEST_POM_TEMPLATE) guava = dependency.new_dep_from_maven_art_str("google:guava:1", "guav") force = dependency.new_dep_from_maven_art_str("force:commons:1", "forc") pomgen.register_dependencies_transitive_closure__artifact(( guava, force, )) generated_pom = pomgen.gen(pom.PomContentType.RELEASE) self.assertIn("<packaging>pom</packaging>", generated_pom) self.assertIn("<dependencyManagement>", generated_pom) self.assertIn("<artifactId>guava</artifactId>", generated_pom) self.assertIn("<artifactId>commons</artifactId>", generated_pom)
def test_external_dependency__unparsable_artifact(self): """ Ensures that external dependency parsing fails when the entire artifact string is invalid. """ artifact = "group:artifact" with self.assertRaises(Exception) as ctx: dependency.new_dep_from_maven_art_str(artifact, "bazel-name") self.assertIn("cannot parse", str(ctx.exception))
def test_equals_hash_code__default_packaging(self): dep1 = dependency.new_dep_from_maven_art_str("com.google.guava:guava:jar:20.0", "name") dep2 = dependency.new_dep_from_maven_art_str("com.google.guava:guava:20.0", "name") s = set() s.add(dep1) s.add(dep2) self.assertEqual(dep1, dep2) self.assertEqual(1, len(s)) self.assertTrue(dep1 in s) self.assertTrue(dep2 in s)
def test_external_dependency__unsupported_version_syntax(self): """ Ensures that external dependency parsing fails when the version string is invalid. """ artifact = "org.glassfish.jersey.ext:jersey-bean-validation:" with self.assertRaises(Exception) as ctx: dependency.new_dep_from_maven_art_str(artifact, "bazel-name") self.assertIn("invalid version", str(ctx.exception))
def test_register_exclusions(self): dependencymd = dependencymdmod.DependencyMetadata() dep1 = dependency.new_dep_from_maven_art_str("g1:a1:v1", "maven1") dep2 = dependency.new_dep_from_maven_art_str("g1:a1:v1", "maven2") exclusions = (dependency.new_dep_from_maven_art_str("t1:a1:v1", "foo")) dependencymd.register_exclusions(dep1, exclusions) dependencymd.register_exclusions(dep2, exclusions) self.assertEqual(exclusions, dependencymd.get_transitive_exclusions(dep1)) self.assertEqual(exclusions, dependencymd.get_transitive_exclusions(dep2))
def test_external_dependency__unparsable_artifact(self): """ Ensures that external dependency parsing fails when the entire artifact string is invalid. """ artifact = "group" with self.assertRaises(Exception) as ctx: dependency.new_dep_from_maven_art_str(artifact, "bazel-name") if sys.version_info[0] < 3: self.assertIn("need more than 1 value to unpack", str(ctx.exception)) else: self.assertIn("(expected 5, got 1)", str(ctx.exception))
def test_sort_order_includes_packaging__one_dep_without_packaging(self): """ Ensures that the optional packaging of an external dependency is included when sorting dependencies. """ dep1 = dependency.new_dep_from_maven_art_str("com.grail.services:arthur-common-thrift-api:jar2:2.2.17", "name1") dep2 = dependency.new_dep_from_maven_art_str("com.grail.services:arthur-common-thrift-api:2.2.17", "name2") dep3 = dependency.new_dep_from_maven_art_str("com.grail.services:arthur-common-thrift-api:jar1:2.2.17", "name3") l = [dep1, dep2, dep3,] l.sort() self.assertIs(dep2, l[0]) self.assertIs(dep3, l[1]) self.assertIs(dep1, l[2])
def test_register_transitives(self): dependencymd = dependencymdmod.DependencyMetadata() dep1 = dependency.new_dep_from_maven_art_str("g1:a1:v1", "maven1") dep2 = dependency.new_dep_from_maven_art_str("g1:a1:v1", "maven2") transitives = (dependency.new_dep_from_maven_art_str( "t1:a1:v1", "foo")) dependencymd.register_transitives(dep1, transitives) dependencymd.register_transitives(dep2, transitives) self.assertEqual(transitives, dependencymd.get_transitive_closure(dep1)) self.assertEqual(transitives, dependencymd.get_transitive_closure(dep2))
def test_equals_hash_code(self): dep1 = dependency.new_dep_from_maven_art_str("com.google.guava:guava:20.0", "name") dep2 = dependency.new_dep_from_maven_art_str("com.google.guava:guava:20.0", "name") dep3 = dependency.new_dep_from_maven_art_str("com.google.guava22:guava:20.0", "name") s = set() s.add(dep1) s.add(dep2) s.add(dep3) self.assertEqual(dep1, dep2) self.assertEqual(2, len(s)) self.assertTrue(dep1 in s) self.assertTrue(dep2 in s) self.assertTrue(dep3 in s)
def _parse_maven_jars(self, external_deps): """ Parses the given external_deps, specified as maven_jar definitions, for example: native.maven_jar( name = "ch_qos_logback_logback_core", artifact = "ch.qos.logback:logback-core:1.2.3", ) Returns a dictionary mapping the value of maven_jar.name to a Dependency instance. """ maven_jar = "maven_jar" name_to_dep = {} maven_jar_index = external_deps.find(maven_jar) while maven_jar_index != -1: name_index = external_deps.find("name", maven_jar_index) if name_index == -1: raise Exception("Didn't find maven_jar's name attribute") art_name, end_quote_index = self._parse_value( name_index, external_deps) artifact_index = external_deps.find("artifact", end_quote_index) if artifact_index == -1: raise Exception("Didn't find maven_jar's artifact attribute") art_value, end_quote_index = self._parse_value( artifact_index, external_deps) assert art_name not in name_to_dep, "duplicate ext dep name %s" % art_name name_to_dep[art_name] = dependency.new_dep_from_maven_art_str( art_value, art_name) maven_jar_index = external_deps.find(maven_jar, end_quote_index) return name_to_dep
def test_external_dependency__marked_as_external(self): """ Ensures the Dependency instance for an external dependency has the 'external' boolean set as expected """ artifact = "group:art:ver" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertTrue(dep.external)
def test_external_dependency__references_artifact(self): """ Ensures the Dependency instance for an external dependency has expected names. """ artifact = "group:art:ver" dep = dependency.new_dep_from_maven_art_str(artifact, "bazel-name") self.assertTrue(dep.references_artifact)
def parse_maven_install(mvn_install_name, json_file_path): """ Returns a list of tuples, one item for each dependency managed by the specified maven_install json file: (dep, transitives, exclusions) dep: dependency.Dependency instance managed by the maven_install rule transitives: for the dep, the transitive closure of dependencies, as list of dependency.Dependency instances exclusions: for the dep, the transitives that are explicitly excluded, as a list of dependency.Dependency instances """ result = [] with open(json_file_path, "r") as f: content = f.read() install_json = json.loads(content) json_dep_tree = install_json["dependency_tree"] conflict_resolution = _parse_conflict_resolution( json_dep_tree, mvn_install_name) json_deps = json_dep_tree["dependencies"] for json_dep in json_deps: coord = json_dep["coord"] dep = dependency.new_dep_from_maven_art_str( coord, mvn_install_name) if dep in conflict_resolution: dep = conflict_resolution[dep] if dep.classifier != "sources": transitives = [] for transitive_gav in json_dep["dependencies"]: transitive_dep = dependency.new_dep_from_maven_art_str( transitive_gav, mvn_install_name) if transitive_dep in conflict_resolution: transitive_dep = conflict_resolution[transitive_dep] transitives.append(transitive_dep) # exclusions only specify group_id:artifact_id - we use # dependency.Dependency instances instead of raw strings for # consistency, but then we need to add a dummy version if "exclusions" in json_dep: exclusions = [ dependency.new_dep_from_maven_art_str( "%s:%s" % (d, dependency.GA_DUMMY_DEP_VERSION), mvn_install_name) for d in json_dep["exclusions"] ] else: exclusions = () result.append((dep, transitives, exclusions)) return result
def test_external_dependency__names__maven_install_name_with_leading_at(self): """ Ensures we handle a maven_install_name with leading "@". """ artifact = "group:art:ver" dep = dependency.new_dep_from_maven_art_str(artifact, "@maven") self.assertEqual("group:art", dep.maven_coordinates_name) self.assertEqual("@maven//:group_art", dep.bazel_label_name) self.assertEqual("group_art", dep.unqualified_bazel_label_name)
def test_external_dependency__with_classifier(self): """ Ensures the Dependency instance for an external dependency has expected classifier. """ artifact = "group:art:packaging:classifier:version" dep = dependency.new_dep_from_maven_art_str(artifact, "bazel-name") self.assertEqual("group:art:classifier", dep.maven_coordinates_name) self.assertEqual("bazel-name", dep.bazel_label_name)
def test_get_ancestors__is_scoped_by_maven_install_rule(self): dependencymd = dependencymdmod.DependencyMetadata() dep1 = dependency.new_dep_from_maven_art_str("g1:a1:v1", "m1") dep2 = dependency.new_dep_from_maven_art_str("g2:a1:v1", "m2") child_dep1 = dependency.new_dep_from_maven_art_str("t1:a1:v1", "m1") child_dep2 = dependency.new_dep_from_maven_art_str("t1:a1:v1", "m2") child_dep3 = dependency.new_dep_from_maven_art_str("t2:a1:v1", "m2") dep1_transitives = [ child_dep1, ] dep2_transitives = [child_dep2, child_dep3] dependencymd.register_transitives(dep1, dep1_transitives) dependencymd.register_transitives(dep2, dep2_transitives) self.assertEqual([dep1], dependencymd.get_ancestors(child_dep1)) self.assertEqual([dep2], dependencymd.get_ancestors(child_dep2)) self.assertEqual([dep2], dependencymd.get_ancestors(child_dep3))
def test_external_dependency__name(self): """ Ensures the Dependency instance for an external dependency has expected names. """ artifact = "group:art:ver" dep = dependency.new_dep_from_maven_art_str(artifact, "bazel-name") self.assertEqual("group:art", dep.maven_coordinates_name) self.assertEqual("bazel-name", dep.bazel_label_name)
def test_external_dependency__names__with_packaging_and_classifier(self): """ Ensures the Dependency instance for an external dependency has expected names when packaging and classifier are set. """ artifact = "group:art:pack:class:version" dep = dependency.new_dep_from_maven_art_str(artifact, "mvn") self.assertEqual("group:art:pack:class", dep.maven_coordinates_name) self.assertEqual("@mvn//:group_art_pack_class", dep.bazel_label_name) self.assertEqual("group_art_pack_class", dep.unqualified_bazel_label_name)
def test_external_dependency__three_coordinates(self): """ Ensures we can create a Dependency instance from a maven coord. """ artifact = "com.google.guava:guava:20.0" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertEqual("com.google.guava", dep.group_id) self.assertEqual("guava", dep.artifact_id) self.assertEqual("20.0", dep.version) self.assertIsNone(dep.classifier) self.assertEqual("jar", dep.packaging) self.assertIsNone(dep.scope)
def test_external_dependency__three_coordinates(self): """ Ensures we can create Dependency instances from maven coordinates, as specified by maven_jar's "artifact" attribute. This test verifies an artifact string with 3 maven coordinates. """ artifact = "com.google.guava:guava:20.0" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertEqual("com.google.guava", dep.group_id) self.assertEqual("guava", dep.artifact_id) self.assertEqual("20.0", dep.version) self.assertIsNone(dep.classifier)
def test_external_dependency__four_coordinates(self): """ Ensures we can create a Dependency instance from a maven coord. """ artifact = "com.grail.log-tokenizer:core-log-tokenizer-api:jar:0.0.21" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertEqual("com.grail.log-tokenizer", dep.group_id) self.assertEqual("core-log-tokenizer-api", dep.artifact_id) self.assertEqual("0.0.21", dep.version) self.assertIsNone(dep.classifier) self.assertEqual("jar", dep.packaging) self.assertIsNone(dep.scope)
def test_external_dependency__five_coordinates(self): """ Ensures we can create a Dependency instance from a maven coord. """ artifact = "com.grail.servicelibs:dynamic-keystore-impl:jar:tests:2.0.39" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertEqual("com.grail.servicelibs", dep.group_id) self.assertEqual("dynamic-keystore-impl", dep.artifact_id) self.assertEqual("2.0.39", dep.version) self.assertEqual("tests", dep.classifier) self.assertEqual("jar", dep.packaging) self.assertIsNone(dep.scope)
def test_external_dependency__four_coordinates(self): """ Ensures we can create Dependency instances from maven coordinates, as specified by maven_jar's "artifact" attribute. This test verifies an artifact string with 4 maven coordinates. """ artifact = "com.grail.log-tokenizer:core-log-tokenizer-api:jar:0.0.21" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertEqual("com.grail.log-tokenizer", dep.group_id) self.assertEqual("core-log-tokenizer-api", dep.artifact_id) self.assertEqual("0.0.21", dep.version) self.assertIsNone(dep.classifier)
def test_external_dependency__five_coordinates(self): """ Ensures we can create Dependency instances from maven coordinates, as specified by maven_jar's "artifact" attribute. This test verifies an artifact string with 5 maven coordinates. """ artifact = "com.grail.servicelibs:dynamic-keystore-impl:jar:tests:2.0.39" dep = dependency.new_dep_from_maven_art_str(artifact, "name") self.assertEqual("com.grail.servicelibs", dep.group_id) self.assertEqual("dynamic-keystore-impl", dep.artifact_id) self.assertEqual("2.0.39", dep.version) self.assertEqual("tests", dep.classifier)
def parse_maven_install(mvn_install_name, json_file_path): """ Returns a list of tuples, one item for each dependency managed by the specified maven_install json file: (dep, transitives, exclusions) dep: dependency.Dependency instance managed by the maven_install rule transitives: for the dep, the transitive closure of dependencies, as list of dependency.Dependency instances exclusions: for the dep, the transitives that are explicitly excluded, as a list of dependency.Dependency instances """ result = [] logger.info("Processing pinned dependencies [%s]" % json_file_path) with open(json_file_path, "r") as f: content = f.read() install_json = json.loads(content) json_deps = install_json["dependency_tree"]["dependencies"] for json_dep in json_deps: coord = json_dep["coord"] dep = dependency.new_dep_from_maven_art_str( coord, mvn_install_name) if dep.classifier != "sources": transitives = [ dependency.new_dep_from_maven_art_str(d, mvn_install_name) for d in json_dep["dependencies"] ] # exclusions only specify group_id:artifact_id - we use # dependency.Dependency instances instead of raw strings for # consistency, but then we need to add a dummy version dummy_version = "-1" if "exclusions" in json_dep: exclusions = [ dependency.new_dep_from_maven_art_str( "%s:%s" % (d, dummy_version), mvn_install_name) for d in json_dep["exclusions"] ] else: exclusions = () result.append((dep, transitives, exclusions)) return result