From f97978738c477e63419135a3560f6e22cd4d34bc Mon Sep 17 00:00:00 2001 From: Oliver Heger Date: Thu, 22 Apr 2021 08:26:29 +0200 Subject: [PATCH] Project: Drop support for a project-local dependency graph Dependency graphs should always be shared between projects, as this is more efficient. Signed-off-by: Oliver Heger --- model/src/main/kotlin/Project.kt | 28 +-------- model/src/test/kotlin/ProjectTest.kt | 91 +++++++--------------------- 2 files changed, 25 insertions(+), 94 deletions(-) diff --git a/model/src/main/kotlin/Project.kt b/model/src/main/kotlin/Project.kt index fc57483ee74aa..53b9c5ada2d9f 100644 --- a/model/src/main/kotlin/Project.kt +++ b/model/src/main/kotlin/Project.kt @@ -95,15 +95,6 @@ data class Project( @JsonProperty("scopes") val scopeDependencies: SortedSet? = null, - /** - * Contains dependency information as a [DependencyGraph]. This is an alternative format to store the dependencies - * referenced by the various scopes. Use the [scopes] property to access dependency information independent on - * the concrete representation. - * TODO: Remove this after all affected package managers have been converted to use a shared graph. - */ - @JsonInclude(JsonInclude.Include.NON_NULL) - val dependencyGraph: DependencyGraph? = null, - /** * Contains dependency information as a set of scope names in case a shared [DependencyGraph] is used. The scopes * of this project and their dependencies can then be constructed as the corresponding sub graph of the shared @@ -131,8 +122,8 @@ data class Project( } init { - require(scopeDependencies == null || dependencyGraph == null) { - "Not both 'scopeDependencies' and 'dependencyGraph' may be set, as otherwise it is ambiguous which one " + + require(scopeDependencies == null || scopeNames == null) { + "Not both 'scopeDependencies' and 'scopeNames' may be set, as otherwise it is ambiguous which one " + "to use." } } @@ -142,19 +133,7 @@ data class Project( * matter whether this information has been initialized directly or has been encoded in a [DependencyGraph]. */ @get:JsonIgnore - val scopes by lazy { - dependencyGraph?.createScopes() ?: scopeDependencies ?: sortedSetOf() - } - - /** - * Return a [Project] instance that has its scope information directly available. A project can be constructed - * either with a set of [Scope] objects or with a [DependencyGraph]. In the latter case, the graph has to be - * converted first into the scope representation. This function ensures that this step was done: If the project - * has a [DependencyGraph], it returns a new instance with the converted scope information (and the dependency - * graph removed to save memory); otherwise, it returns this same object. - */ - fun withResolvedScopes(): Project = - takeUnless { dependencyGraph != null } ?: copy(scopeDependencies = scopes, dependencyGraph = null) + val scopes by lazy { scopeDependencies ?: sortedSetOf() } /** * Return a [Project] instance that has its scope information directly available, resolved from the given [graph]. @@ -166,7 +145,6 @@ data class Project( takeUnless { graph != null && scopeNames != null } ?: copy( scopeDependencies = graph!!.createScopes(qualifiedScopeNames()), - dependencyGraph = null, scopeNames = null ) diff --git a/model/src/test/kotlin/ProjectTest.kt b/model/src/test/kotlin/ProjectTest.kt index a814112a337f4..992e22c6c1c27 100644 --- a/model/src/test/kotlin/ProjectTest.kt +++ b/model/src/test/kotlin/ProjectTest.kt @@ -20,21 +20,21 @@ package org.ossreviewtoolkit.model import io.kotest.assertions.fail +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.WordSpec import io.kotest.matchers.collections.beEmpty import io.kotest.matchers.collections.containExactlyInAnyOrder import io.kotest.matchers.collections.shouldBeEmpty -import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.nulls.beNull -import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.should import io.kotest.matchers.shouldBe import io.kotest.matchers.types.beTheSameInstanceAs +import io.mockk.mockk + import java.io.File import java.time.Instant -import java.util.SortedSet import org.ossreviewtoolkit.utils.test.containExactly @@ -52,20 +52,6 @@ private val langId = Identifier("$MANAGER:org.apache.commons:commons-lang3:3.5") private val strutsId = Identifier("$MANAGER:org.apache.struts:struts2-assembly:2.5.14.1") private val csvId = Identifier("$MANAGER:org.apache.commons:commons-csv:1.4") -/** - * Create a [Project] whose dependencies are represented as a [DependencyGraph]. - */ -private fun projectWithDependencyGraph(): Project = - Project( - id = projectId, - definitionFilePath = "/some/path", - declaredLicenses = sortedSetOf(), - vcs = VcsInfo.EMPTY, - homepageUrl = "https//www.test-project.org", - scopeDependencies = null, - dependencyGraph = createDependencyGraph() - ) - /** * Create a [DependencyGraph] containing some test dependencies, optionally with [qualified] scope names. */ @@ -100,18 +86,23 @@ private fun createDependencyGraph(qualified: Boolean = false): DependencyGraph { */ private fun Identifier.toDependencyId() = "$MANAGER:$namespace:$name:$version" -/** - * Lookup the scope with the given [name] in the set of [scopes]. - */ -private fun findScope(scopes: SortedSet, name: String): Scope = - scopes.find { it.name == name } ?: fail("Could not resolve scope $name.") - -/** - * Return a set with the identifiers of the (direct) dependencies of the given [scope]. - */ -private fun scopeDependencies(scope: Scope): Set = scope.dependencies.map { it.id }.toSet() - class ProjectTest : WordSpec({ + "init" should { + "fail if both scopeDependencies and scopeNames are provided" { + shouldThrow { + Project( + id = projectId, + definitionFilePath = "/some/path/pom.xml", + declaredLicenses = sortedSetOf(), + vcs = VcsInfo.EMPTY, + homepageUrl = "https://test-project.example.org/home.html", + scopeDependencies = sortedSetOf(mockk()), + scopeNames = sortedSetOf("test", "compile", "other") + ) + } + } + } + "collectDependencies" should { "get all dependencies by default" { val project = readAnalyzerResult("gradle-expected-output-lib.yml") @@ -182,19 +173,6 @@ class ProjectTest : WordSpec({ project.scopes shouldBe project.scopeDependencies } - "be initialized from a dependency graph" { - val project = projectWithDependencyGraph() - val scopes = project.scopes - scopes.map { it.name } shouldContainExactly listOf("compile", "default", "partial", "test") - - val defaultScope = findScope(scopes, "default") - scopeDependencies(defaultScope) should io.kotest.matchers.collections.containExactly(exampleId) - val testScope = findScope(scopes, "test") - scopeDependencies(testScope) should containExactlyInAnyOrder(exampleId, csvId) - val partialScope = findScope(scopes, "partial") - scopeDependencies(partialScope) should io.kotest.matchers.collections.containExactly(textId) - } - "be initialized to an empty set if no information is available" { val project = Project( id = projectId, @@ -202,8 +180,6 @@ class ProjectTest : WordSpec({ declaredLicenses = sortedSetOf(), vcs = VcsInfo.EMPTY, homepageUrl = "https//www.test-project.org", - scopeDependencies = null, - dependencyGraph = null ) project.scopes.shouldBeEmpty() @@ -211,23 +187,14 @@ class ProjectTest : WordSpec({ } "withResolvedScopes" should { - "return the same instance if no dependency graph is available" { + "return the same instance if scope dependencies are available" { val project = readAnalyzerResult("maven-expected-output-app.yml") - val resolvedProject = project.withResolvedScopes() + val resolvedProject = project.withResolvedScopes(createDependencyGraph()) resolvedProject should beTheSameInstanceAs(project) } - "return an instance with scope information extracted from the dependency graph" { - val project = projectWithDependencyGraph() - - val resolvedProject = project.withResolvedScopes() - - resolvedProject.dependencyGraph.shouldBeNull() - resolvedProject.scopes shouldBe project.scopes - } - "return an instance with scope information extracted from a sub graph of a shared dependency graph" { val project = Project( id = projectId, @@ -245,21 +212,7 @@ class ProjectTest : WordSpec({ resolvedProject.scopeNames should beNull() resolvedProject.scopes shouldHaveSize 1 - findScope(resolvedProject.scopes, "partial") - } - } - - "A Project" should { - "be serializable with a dependency graph" { - val outputFile = kotlin.io.path.createTempFile(prefix = "project", suffix = ".yml").toFile().apply { - deleteOnExit() - } - - val project = projectWithDependencyGraph() - outputFile.writeValue(project) - - val projectCopy = outputFile.readValue() - projectCopy.scopes shouldBe project.scopes + resolvedProject.scopes.find { it.name == "partial" } ?: fail("Could not resolve scope ${"partial"}.") } } })