mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[python] PY-83776: Python list can't be sorted due to "Comparison method violates its general contract!".
`com.intellij.python.community.services.shared.PythonInfoWithUiComparator` sorts by `PythonInfoComparator` and then by `UiComparator`. It used to multiply first result to `10` trying to shift it one digit left, but `compare` contract doesn't guarantee anything except the sign. It could be `-233` for certain tool names (i.e: different lengths, different characters with a huge distance between their codes). We now only check contract Merge-request: IJ-MR-186470 Merged-by: Ilya Kazakevich <ilya.kazakevich@jetbrains.com> GitOrigin-RevId: 89e9b3de2bd0e4b46d1c213f3b262dd698462275
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1e71902271
commit
5d4fd17a0f
+5
-1
@@ -32,7 +32,11 @@ class PythonInfoWithUiComparator<T> : Comparator<T> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-1
@@ -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<MockInfo>()
|
||||
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<MockInfo> {
|
||||
|
||||
override fun compareTo(other: MockInfo): Int = COMPARATOR.compare(this, other)
|
||||
}
|
||||
|
||||
private val COMPARATOR = PythonInfoWithUiComparator<MockInfo>()
|
||||
|
||||
Reference in New Issue
Block a user