Skip to content

Commit 029336c

Browse files
committed
analyzer: Stop using a SortedSet in ProjectAnalyzerResult
The `ProjectAnalyzerResult` is only serialized as part of analyzer functional tests when comparing to expected results. So in memory, there is no need for the `packages` to be a `SortedSet`. Change the code to only sort `packages` on serialization via a Jackson converter to have a defined order when comparing to expected results. This lays the fundation for getting rid of `Comparable` implementations in various classes. These implementations were only added for sorted output during serialization, but they break the `Set` contract as `compareTo() == 0` is not aligned with `equals() == true`, also see [1]. [1]: https://docs.oracle.com/javase/8/docs/api/java/util/SortedSet.html Signed-off-by: Sebastian Schuberth <[email protected]>
1 parent 32fce9e commit 029336c

28 files changed

+76
-46
lines changed

analyzer/src/funTest/kotlin/managers/Extensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ fun PackageManagerResult.resolveScopes(projectResult: ProjectAnalyzerResult): Pr
8383
// all packages; this handles corner cases with package managers producing packages not assigned to project scopes.
8484
val packages = projectResult.packages.takeUnless { it.isEmpty() }
8585
?: if (projectResults.size > 1) resolvedProject.filterReferencedPackages(sharedPackages) else sharedPackages
86-
return projectResult.copy(project = resolvedProject, packages = packages.toSortedSet())
86+
return projectResult.copy(project = resolvedProject, packages = packages)
8787
}
8888

8989
/**

analyzer/src/funTest/kotlin/managers/SpdxDocumentFileFunTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class SpdxDocumentFileFunTest : WordSpec({
116116
Scope("default")
117117
)
118118
),
119-
sortedSetOf()
119+
emptySet()
120120
)
121121

122122
actualResult[opensslId] shouldBe ProjectAnalyzerResult(
@@ -137,7 +137,7 @@ class SpdxDocumentFileFunTest : WordSpec({
137137
Scope("default")
138138
)
139139
),
140-
sortedSetOf()
140+
emptySet()
141141
)
142142

143143
actualResult[zlibId] shouldBe ProjectAnalyzerResult(
@@ -158,7 +158,7 @@ class SpdxDocumentFileFunTest : WordSpec({
158158
Scope("default")
159159
)
160160
),
161-
sortedSetOf()
161+
emptySet()
162162
)
163163
}
164164

analyzer/src/main/kotlin/PackageManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ abstract class PackageManager(
292292
)
293293
)
294294

295-
result[definitionFile] = listOf(ProjectAnalyzerResult(projectWithIssues, sortedSetOf(), issues))
295+
result[definitionFile] = listOf(ProjectAnalyzerResult(projectWithIssues, emptySet(), issues))
296296
}
297297
}
298298

@@ -333,7 +333,7 @@ abstract class PackageManager(
333333
entry.value.map { projectResult ->
334334
val projectReferences = projectResult.packages.filterTo(mutableSetOf()) { it.id in projectIds }
335335
projectResult.takeIf { projectReferences.isEmpty() }
336-
?: projectResult.copy(packages = (projectResult.packages - projectReferences).toSortedSet())
336+
?: projectResult.copy(packages = projectResult.packages - projectReferences)
337337
.also {
338338
logger.info { "Removing ${projectReferences.size} packages that are projects." }
339339

analyzer/src/main/kotlin/PackageManagerResult.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ data class PackageManagerResult(
4848
* produce a shared [DependencyGraph] typically do not collect packages on a project-level, but globally. Such
4949
* packages can be stored in this property.
5050
*/
51-
val sharedPackages: Set<Package> = sortedSetOf()
51+
val sharedPackages: Set<Package> = emptySet()
5252
)

analyzer/src/main/kotlin/managers/Bower.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ class Bower(
258258
scopeDependencies = sortedSetOf(dependenciesScope, devDependenciesScope)
259259
)
260260

261-
return listOf(ProjectAnalyzerResult(project, packages.values.toSortedSet()))
261+
return listOf(ProjectAnalyzerResult(project, packages.values.toSet()))
262262
}
263263
}
264264

analyzer/src/main/kotlin/managers/Bundler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ class Bundler(
205205
val allProjectDeps = groupedDeps.values.flatten().toSet()
206206
val hasBundlerDep = BUNDLER_GEM_NAME in allProjectDeps
207207

208-
val packages = gemSpecs.values.mapNotNullTo(sortedSetOf()) { gemSpec ->
208+
val packages = gemSpecs.values.mapNotNullTo(mutableSetOf()) { gemSpec ->
209209
getPackageFromGemspec(gemSpec).takeUnless { gemSpec.name == BUNDLER_GEM_NAME && !hasBundlerDep }
210210
}
211211

analyzer/src/main/kotlin/managers/Cargo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class Cargo(
237237

238238
val nonProjectPackages = packages
239239
.filterNot { isProjectDependency(it.key) }
240-
.mapTo(sortedSetOf()) { it.value }
240+
.mapTo(mutableSetOf()) { it.value }
241241

242242
return listOf(ProjectAnalyzerResult(project, nonProjectPackages))
243243
}

analyzer/src/main/kotlin/managers/Carthage.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import com.fasterxml.jackson.module.kotlin.readValue
2323

2424
import java.io.File
2525
import java.net.URL
26-
import java.util.SortedSet
2726

2827
import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory
2928
import org.ossreviewtoolkit.analyzer.PackageManager
@@ -111,10 +110,10 @@ class Carthage(
111110
)
112111
}
113112

114-
private fun parseCarthageDependencies(definitionFile: File): SortedSet<Package> {
113+
private fun parseCarthageDependencies(definitionFile: File): Set<Package> {
115114
val dependencyLines = definitionFile.readLines()
116115
val workingDir = definitionFile.parent
117-
val packages = sortedSetOf<Package>()
116+
val packages = mutableSetOf<Package>()
118117

119118
dependencyLines.forEach { line ->
120119
if (line.isBlank() || line.isComment()) return@forEach

analyzer/src/main/kotlin/managers/CocoaPods.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class CocoaPods(
132132
val lockfile = workingDir.resolve(LOCKFILE_FILENAME)
133133

134134
val scopes = sortedSetOf<Scope>()
135-
val packages = sortedSetOf<Package>()
135+
val packages = mutableSetOf<Package>()
136136
val issues = mutableListOf<OrtIssue>()
137137

138138
if (lockfile.isFile) {

analyzer/src/main/kotlin/managers/Composer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ class Composer(
161161

162162
val project = parseProject(definitionFile, scopes)
163163

164-
return listOf(ProjectAnalyzerResult(project, packages.values.toSortedSet()))
164+
return listOf(ProjectAnalyzerResult(project, packages.values.toSet()))
165165
}
166166
}
167167

analyzer/src/main/kotlin/managers/Conan.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class Conan(
179179
homepageUrl = projectPackage.homepageUrl,
180180
scopeDependencies = sortedSetOf(dependenciesScope, devDependenciesScope)
181181
),
182-
packages = packages.values.toSortedSet()
182+
packages = packages.values.toSet()
183183
)
184184
)
185185
}

analyzer/src/main/kotlin/managers/GoDep.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class GoDep(
109109
}
110110

111111
val projects = parseProjects(workingDir, gopath)
112-
val packages = sortedSetOf<Package>()
112+
val packages = mutableSetOf<Package>()
113113
val packageRefs = mutableListOf<PackageReference>()
114114

115115
for (project in projects) {

analyzer/src/main/kotlin/managers/GoMod.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class GoMod(
105105
val graph = getModuleGraph(projectDir, moduleInfoForModuleName)
106106
val projectId = graph.projectId()
107107
val packageIds = graph.nodes() - projectId
108-
val packages = packageIds.mapTo(sortedSetOf()) { moduleInfoForModuleName.getValue(it.name).toPackage() }
108+
val packages = packageIds.mapTo(mutableSetOf()) { moduleInfoForModuleName.getValue(it.name).toPackage() }
109109
val projectVcs = processProjectVcs(projectDir)
110110

111111
val dependenciesScopePackageIds = getTransitiveMainModuleDependencies(projectDir).let { moduleNames ->

analyzer/src/main/kotlin/managers/Gradle.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ class Gradle(
276276
createAndLogIssue(source = managerName, message = it, severity = Severity.WARNING)
277277
}
278278

279-
listOf(ProjectAnalyzerResult(project, sortedSetOf(), issues))
279+
listOf(ProjectAnalyzerResult(project, emptySet(), issues))
280280
}
281281
}
282282
}

analyzer/src/main/kotlin/managers/Maven.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class Maven(
164164
}
165165
}
166166

167-
return listOf(ProjectAnalyzerResult(project, sortedSetOf(), issues))
167+
return listOf(ProjectAnalyzerResult(project, emptySet(), issues))
168168
}
169169
}
170170

analyzer/src/main/kotlin/managers/Npm.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ open class Npm(
209209

210210
// TODO: add support for peerDependencies and bundledDependencies.
211211

212-
return listOf(ProjectAnalyzerResult(project.copy(scopeNames = scopeNames.toSortedSet()), sortedSetOf()))
212+
return listOf(ProjectAnalyzerResult(project.copy(scopeNames = scopeNames.toSortedSet()), emptySet()))
213213
}
214214

215215
private fun parseInstalledModules(rootDirectory: File): Map<String, Package> {

analyzer/src/main/kotlin/managers/Pub.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class Pub(
262262

263263
val project = parseProject(definitionFile, manifest, scopes)
264264

265-
projectAnalyzerResults += ProjectAnalyzerResult(project, packages.values.toSortedSet(), issues)
265+
projectAnalyzerResults += ProjectAnalyzerResult(project, packages.values.toSet(), issues)
266266

267267
return projectAnalyzerResults
268268
}
@@ -395,7 +395,7 @@ class Pub(
395395
.resolveDependencies(listOf(packageFile), labels).run {
396396
projectResults.getValue(packageFile).map { result ->
397397
val project = result.project.withResolvedScopes(dependencyGraph)
398-
result.copy(project = project, packages = sharedPackages.toSortedSet())
398+
result.copy(project = project, packages = sharedPackages)
399399
}
400400
}
401401
}
@@ -422,7 +422,7 @@ class Pub(
422422
"implemented."
423423
)
424424

425-
return ProjectAnalyzerResult(Project.EMPTY, sortedSetOf(), listOf(issue))
425+
return ProjectAnalyzerResult(Project.EMPTY, emptySet(), listOf(issue))
426426
}
427427

428428
private fun parseProject(definitionFile: File, pubspec: JsonNode, scopes: SortedSet<Scope>): Project {

analyzer/src/main/kotlin/managers/SpdxDocumentFile.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ class SpdxDocumentFile(
523523
scopeDependencies = scopes
524524
)
525525

526-
return listOf(ProjectAnalyzerResult(project, packages.toSortedSet()))
526+
return listOf(ProjectAnalyzerResult(project, packages))
527527
}
528528

529529
/**

analyzer/src/main/kotlin/managers/Stack.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class Stack(
221221
scopeDependencies = scopes
222222
)
223223

224-
return listOf(ProjectAnalyzerResult(project, dependencyPackageMap.values.toSortedSet()))
224+
return listOf(ProjectAnalyzerResult(project, dependencyPackageMap.values.toSet()))
225225
}
226226

227227
private fun getPackageUrl(name: String, version: String) =

analyzer/src/main/kotlin/managers/Unmanaged.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class Unmanaged(
113113
return listOf(
114114
ProjectAnalyzerResult(
115115
project = Project.EMPTY.copy(id = id, vcsProcessed = vcsInfo),
116-
packages = sortedSetOf()
116+
packages = emptySet()
117117
)
118118
)
119119
}

analyzer/src/main/kotlin/managers/Yarn2.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ class Yarn2(
236236
val allProjects = parseAllPackages(iterator, definitionFile, packagesHeaders, packagedDetails)
237237
val scopeNames = YarnDependencyType.values().map { it.type }.toSortedSet()
238238
return allProjects.values.map { project ->
239-
ProjectAnalyzerResult(project.copy(scopeNames = scopeNames), sortedSetOf(), issues)
239+
ProjectAnalyzerResult(project.copy(scopeNames = scopeNames), emptySet(), issues)
240240
}.toList()
241241
}
242242
}

analyzer/src/main/kotlin/managers/utils/NuGetSupport.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ class NuGetSupport(
240240
fun resolveDependencies(definitionFile: File, directDependenciesOnly: Boolean): ProjectAnalyzerResult {
241241
val workingDir = definitionFile.parentFile
242242

243-
val packages = sortedSetOf<Package>()
243+
val packages = mutableSetOf<Package>()
244244

245245
val references = reader.getDependencies(definitionFile)
246246
val referencesByFramework = references.groupBy { it.targetFramework }

analyzer/src/main/kotlin/managers/utils/PythonInspector.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ private fun processDeclaredLicenses(id: Identifier, declaredLicenses: SortedSet<
286286
return declaredLicensesProcessed
287287
}
288288

289-
internal fun List<PythonInspector.Package>.toOrtPackages(): SortedSet<Package> =
290-
groupBy { "${it.name}:${it.version}" }.mapTo(sortedSetOf()) { (_, packages) ->
289+
internal fun List<PythonInspector.Package>.toOrtPackages(): Set<Package> =
290+
groupBy { "${it.name}:${it.version}" }.mapTo(mutableSetOf()) { (_, packages) ->
291291
// The python inspector currently often contains two entries for a package where the only difference is the
292292
// download URL. In this case, one package contains the URL of the binary artifact, the other for the source
293293
// artifact. So take all metadata from the first package except for the artifacts.

analyzer/src/test/kotlin/AnalyzerResultBuilderTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ class AnalyzerResultBuilderTest : WordSpec() {
111111
)
112112

113113
private val analyzerResult1 = ProjectAnalyzerResult(
114-
project1, sortedSetOf(package1), listOf(issue3, issue4)
114+
project1, setOf(package1), listOf(issue3, issue4)
115115
)
116116
private val analyzerResult2 = ProjectAnalyzerResult(
117-
project2, sortedSetOf(package1, package2, package3), listOf(issue4)
117+
project2, setOf(package1, package2, package3), listOf(issue4)
118118
)
119119

120120
init {
@@ -236,10 +236,10 @@ class AnalyzerResultBuilderTest : WordSpec() {
236236
val p1 = project1.copy(scopeDependencies = null, scopeNames = sortedSetOf("scope-1"))
237237
val p2 = project2.copy(scopeDependencies = null, scopeNames = sortedSetOf("scope-3"))
238238
val analyzerResult = AnalyzerResultBuilder()
239-
.addResult(ProjectAnalyzerResult(p1, sortedSetOf()))
239+
.addResult(ProjectAnalyzerResult(p1, emptySet()))
240240
.addDependencyGraph(p1.id.type, graph1)
241-
.addResult(ProjectAnalyzerResult(project3, sortedSetOf()))
242-
.addResult(ProjectAnalyzerResult(p2, sortedSetOf()))
241+
.addResult(ProjectAnalyzerResult(project3, emptySet()))
242+
.addResult(ProjectAnalyzerResult(p2, emptySet()))
243243
.addDependencyGraph(p2.id.type, graph2)
244244
.build()
245245

@@ -333,7 +333,7 @@ class AnalyzerResultBuilderTest : WordSpec() {
333333

334334
val projectAnalyzerResult = ProjectAnalyzerResult(
335335
project = project,
336-
packages = sortedSetOf(package1)
336+
packages = setOf(package1)
337337
)
338338

339339
val analyzerResult = AnalyzerResultBuilder().run {

model/src/main/kotlin/Package.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ data class Package(
130130
*/
131131
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
132132
val isModified: Boolean = false
133-
) : Comparable<Package> {
133+
) {
134134
companion object {
135135
/**
136136
* A constant for a [Package] where all properties are empty.
@@ -152,11 +152,6 @@ data class Package(
152152
)
153153
}
154154

155-
/**
156-
* A comparison function to sort packages by their identifier.
157-
*/
158-
override fun compareTo(other: Package) = id.compareTo(other.id)
159-
160155
/**
161156
* Compares this package with [other] and creates a [PackageCurationData] containing the values from this package
162157
* which are different in [other]. All equal values are set to null. Only the fields present in

model/src/main/kotlin/ProjectAnalyzerResult.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
package org.ossreviewtoolkit.model
2121

2222
import com.fasterxml.jackson.annotation.JsonInclude
23+
import com.fasterxml.jackson.databind.annotation.JsonSerialize
2324

24-
import java.util.SortedSet
25+
import org.ossreviewtoolkit.model.utils.PackageSortedSetConverter
2526

2627
/**
2728
* A class that bundles all information generated during an analysis.
@@ -36,7 +37,8 @@ data class ProjectAnalyzerResult(
3637
/**
3738
* The set of identified packages used by the project.
3839
*/
39-
val packages: SortedSet<Package>,
40+
@JsonSerialize(converter = PackageSortedSetConverter::class)
41+
val packages: Set<Package>,
4042

4143
/**
4244
* The list of issues that occurred during dependency resolution. Defaults to an empty list.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (C) 2022 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
@file:Suppress("Filename", "MatchingDeclarationName")
21+
22+
package org.ossreviewtoolkit.model.utils
23+
24+
import com.fasterxml.jackson.databind.util.StdConverter
25+
26+
import java.util.SortedSet
27+
28+
import org.ossreviewtoolkit.model.Package
29+
30+
class PackageSortedSetConverter : StdConverter<Set<Package>, SortedSet<Package>>() {
31+
override fun convert(value: Set<Package>) = value.toSortedSet(compareBy { it.id })
32+
}
33+
34+
// TODO: Add more converters to get rid of Comparable implementations that just serve sorted output.

model/src/test/kotlin/utils/DependencyGraphConverterTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ private fun createAnalyzerResult(vararg projectResults: ProjectAnalyzerResult):
203203
* Create a [ProjectAnalyzerResult] based on this test project.
204204
*/
205205
private fun Project.createResult(): ProjectAnalyzerResult {
206-
val packages = scopes.flatMap { it.dependencies }.mapTo(sortedSetOf()) { ref ->
206+
val packages = scopes.flatMap { it.dependencies }.mapTo(mutableSetOf()) { ref ->
207207
Package.EMPTY.copy(id = ref.id)
208208
}
209209

0 commit comments

Comments
 (0)