diff --git a/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt b/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt index 501e8ec6127e..95e86bbad26f 100644 --- a/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt +++ b/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt @@ -32,7 +32,11 @@ class PythonInfoWithUiComparator : Comparator where T : PythonInfoHolder, if (logger.isTraceEnabled) { logger.trace("full ${o1.string()} vs ${o2.string()}") } - return PythonInfoComparator.compare(o1, o2) * 10 + UiComparator.compare(o1, o2) + val infoResult = PythonInfoComparator.compare(o1, o2) + if (infoResult != 0) { + return infoResult + } + return UiComparator.compare(o1, o2) } } diff --git a/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt b/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt index 28b34ea4d83f..7b6b16741679 100644 --- a/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt +++ b/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt @@ -9,6 +9,7 @@ import com.jetbrains.python.PythonInfo import com.jetbrains.python.psi.LanguageLevel import org.hamcrest.MatcherAssert import org.hamcrest.Matchers +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import java.util.* @@ -41,9 +42,43 @@ class ComparatorsTest { MockInfo(PythonInfo(LanguageLevel.PYTHON27)) )) } + + /** + * Check [Comparator.compare] contract for [PythonInfoWithUiComparator] + */ + @Test + fun testCompareContract() { + val longName = " ".repeat(100) + val data = mutableListOf() + for (lang in LanguageLevel.entries) { + for (free in arrayOf(true, false)) { + for (title in arrayOf(null, "uv", "homebrew", " ", "ΓΏ", longName)) { + val ui = if (title == null) null else PyToolUIInfo(title) + data.add(MockInfo(PythonInfo(lang, free), ui)) + } + } + } + + for (x in data) { + Assertions.assertEquals(0, x.compareTo(x), "Reflexivity broken for $x") + for (y in data) { + Assertions.assertEquals(-(y.compareTo(x)), x.compareTo(y), "Antisymmetry broken for $x, $y") + for (z in data) { + if (x > y && y > z) { + Assertions.assertTrue(x > z, "Transitivity broken for $x, $y, $z") + } + } + } + } + } } private data class MockInfo( override val pythonInfo: PythonInfo, override val ui: PyToolUIInfo? = null, -) : PythonInfoHolder, UiHolder +) : PythonInfoHolder, UiHolder, Comparable { + + override fun compareTo(other: MockInfo): Int = COMPARATOR.compare(this, other) +} + +private val COMPARATOR = PythonInfoWithUiComparator()