Skip to content

Commit dfb5f87

Browse files
committed
feat(scanner): Merge duplicate scan results that share a provenance
When the SpdxDocumentFile package manager is used, the *project* and all contained *packages* often resolve to the **same VCS provenance** (e.g. the root of the Git repository). Before this change ORT stored two separate `ScanResult`s for such a provenance – one keyed to the project, one keyed to the package. That caused two follow-on problems: * Both results appeared in the `OrtResult`, so evaluators saw **duplicate findings** for the *same* source tree. * Because projects and packages are handled by different rules the package result was additionally **padded with a `SpdxConstants.NONE` finding** whenever `includeFilesWithoutFindings` was enabled. The evaluator therefore compared *real* license findings from the project result with `NONE` from the package result and failed with a violation. This patch * groups scan results by the pair `(provenance, scanner)` and folds them into a single `ScanResult`, * unions the inner finding sets to avoid duplicates, and * performs the "pad with NONE" step only **after** deduplication, so every path is represented exactly once. As a consequence the evaluator now receives one consistent set of license findings per provenance / scanner, eliminating the false mismatch. Signed-off-by: Jonatan Männchen <jonatan@maennchen.ch>
1 parent 7e8d90c commit dfb5f87

File tree

3 files changed

+408
-30
lines changed

3 files changed

+408
-30
lines changed

scanner/src/funTest/kotlin/scanners/ScannerIntegrationFunTest.kt

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.ossreviewtoolkit.model.Package
3434
import org.ossreviewtoolkit.model.PackageReference
3535
import org.ossreviewtoolkit.model.PackageType
3636
import org.ossreviewtoolkit.model.Project
37+
import org.ossreviewtoolkit.model.Repository
3738
import org.ossreviewtoolkit.model.ScanSummary
3839
import org.ossreviewtoolkit.model.Scope
3940
import org.ossreviewtoolkit.model.TextLocation
@@ -100,6 +101,18 @@ class ScannerIntegrationFunTest : WordSpec({
100101
patchExpectedResult(expectedResult)
101102
}
102103
}
104+
105+
"Scanning a project with the same provenance as packages" should {
106+
val analyzerResult = createAnalyzerResultWithProject(project0, pkg0)
107+
val ortResult = createScanner().scan(analyzerResult, skipExcluded = false, emptyMap())
108+
109+
"not have duplicated scan results" {
110+
val expectedResult = readResource("/scanner-integration-shared-project-package-provenance.yml")
111+
112+
patchActualResult(ortResult.toYaml(), patchStartAndEndTime = true) shouldBe
113+
patchExpectedResult(expectedResult)
114+
}
115+
}
103116
})
104117

105118
internal fun createScanner(scannerWrappers: Map<PackageType, List<ScannerWrapper>>? = null): Scanner {
@@ -128,25 +141,38 @@ internal fun createScanner(scannerWrappers: Map<PackageType, List<ScannerWrapper
128141
}
129142

130143
private fun createAnalyzerResult(vararg packages: Package): OrtResult {
144+
val project = Project.EMPTY.copy(
145+
id = createId("project")
146+
)
147+
148+
return createAnalyzerResultWithProject(project, *packages)
149+
}
150+
151+
private fun createAnalyzerResultWithProject(project: Project, vararg packages: Package): OrtResult {
131152
val scope = Scope(
132153
name = "deps",
133154
dependencies = packages.mapTo(mutableSetOf()) { PackageReference(it.id) }
134155
)
135156

136-
val project = Project.EMPTY.copy(
137-
id = createId("project"),
157+
val projectWithScope = project.copy(
138158
scopeDependencies = setOf(scope)
139159
)
140160

141161
val analyzerRun = AnalyzerRun.EMPTY.copy(
142162
result = AnalyzerResult.EMPTY.copy(
143-
projects = setOf(project),
163+
projects = setOf(projectWithScope),
144164
packages = packages.toSet()
145165
),
146166
config = AnalyzerConfiguration(enabledPackageManagers = emptyList())
147167
)
148168

149-
return OrtResult.EMPTY.copy(analyzer = analyzerRun)
169+
return OrtResult.EMPTY.copy(
170+
analyzer = analyzerRun,
171+
repository = Repository.EMPTY.copy(
172+
vcsProcessed = projectWithScope.vcsProcessed,
173+
vcs = projectWithScope.vcs
174+
)
175+
)
150176
}
151177

152178
private fun createId(name: String): Identifier = Identifier("Dummy::$name:1.0.0")
@@ -158,6 +184,23 @@ private fun createPackage(name: String, vcs: VcsInfo): Package =
158184
vcsProcessed = vcs.normalize()
159185
)
160186

187+
private fun createProject(name: String, vcs: VcsInfo): Project =
188+
Project.EMPTY.copy(
189+
id = createId(name),
190+
vcs = vcs,
191+
vcsProcessed = vcs.normalize()
192+
)
193+
194+
private val project0 = createProject(
195+
name = "project",
196+
vcs = VcsInfo(
197+
type = VcsType.GIT,
198+
url = "https://github.com/oss-review-toolkit/ort-test-data-scanner.git",
199+
revision = "97d57bb4795bc41f496e1a8e2c7751cefc7da7ec",
200+
path = ""
201+
)
202+
)
203+
161204
// A package with an empty VCS path.
162205
private val pkg0 = createPackage(
163206
name = "pkg0",

0 commit comments

Comments
 (0)