Use a PairingListener class to collect or print weights, avoid computing twice the weights during tests
This commit is contained in:
@@ -20,24 +20,22 @@ class BOSP2024Test: TestBase() {
|
||||
)!!.asObject()
|
||||
val resp = TestAPI.post("/api/tour", tournament).asObject()
|
||||
val tourId = resp.getInt("id")
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("bosp2024-weights.txt")))
|
||||
|
||||
Solver.legacy_mode = true
|
||||
TestAPI.post("/api/tour/$tourId/pair/3", Json.Array("all")).asArray()
|
||||
val outputFile = getOutputFile("bosp2024-weights.txt")
|
||||
TestAPI.post("/api/tour/$tourId/pair/3?legacy=true&weights_output=$outputFile", Json.Array("all")).asArray()
|
||||
|
||||
// compare weights
|
||||
assertTrue(compare_weights(getOutputFile("bosp2024-weights.txt"), getTestFile("opengotha/bosp2024/bosp2024_weights_R3.txt")), "Not matching opengotha weights for BOSP test")
|
||||
assertTrue(compare_weights(outputFile, getTestFile("opengotha/bosp2024/bosp2024_weights_R3.txt")), "Not matching opengotha weights for BOSP test")
|
||||
TestAPI.delete("/api/tour/$tourId/pair/3", Json.Array("all"))
|
||||
|
||||
Solver.legacy_mode = false
|
||||
val games = TestAPI.post("/api/tour/$tourId/pair/3", Json.Array("all")).asArray()
|
||||
// Aksut Husrev is ID 18
|
||||
val solved = games.map { it as Json.Object }.filter { game ->
|
||||
val solved = games.map { it as Json.Object }.firstOrNull { game ->
|
||||
// build the two-elements set of players ids
|
||||
val players = game.entries.filter { (k, v) -> k == "b" || k == "w" }.map { (k, v) -> (v as Number).toInt() }.toSet()
|
||||
val players =
|
||||
game.entries.filter { (k, v) -> k == "b" || k == "w" }.map { (k, v) -> (v as Number).toInt() }.toSet()
|
||||
// keep game with Aksut Husrev
|
||||
players.contains(18)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
assertNotNull(solved)
|
||||
|
||||
|
@@ -1,11 +1,8 @@
|
||||
package org.jeudego.pairgoth.test
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.pairing.solver.Solver
|
||||
import org.jeudego.pairgoth.test.PairingTests.Companion.compare_weights
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
@@ -19,8 +16,8 @@ class MalavasiTest: TestBase() {
|
||||
)!!.asObject()
|
||||
val resp = TestAPI.post("/api/tour", tournament).asObject()
|
||||
val tourId = resp.getInt("id")
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("malavasi-weights.txt")))
|
||||
val games = TestAPI.post("/api/tour/$tourId/pair/2", Json.Array("all")).asArray()
|
||||
val outputFile = getOutputFile("malavasi-weights.txt")
|
||||
val games = TestAPI.post("/api/tour/$tourId/pair/2?weights_output=$outputFile", Json.Array("all")).asArray()
|
||||
// Oceane is ID 548, Valentine 549
|
||||
val buggy = games.map { it as Json.Object }.filter { game ->
|
||||
// build the two-elements set of players ids
|
||||
@@ -33,6 +30,6 @@ class MalavasiTest: TestBase() {
|
||||
assertEquals(2, buggy.size)
|
||||
|
||||
// compare weights
|
||||
assertTrue(compare_weights(getOutputFile("malavasi-weights.txt"), getTestFile("opengotha/malavasi/malavasi_weights_R2.txt")), "Not matching opengotha weights for Malavasi test")
|
||||
assertTrue(compare_weights(outputFile, getTestFile("opengotha/malavasi/malavasi_weights_R2.txt")), "Not matching opengotha weights for Malavasi test")
|
||||
}
|
||||
}
|
||||
|
@@ -8,8 +8,6 @@ import org.jeudego.pairgoth.store.lastPlayerId
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.abs
|
||||
@@ -59,7 +57,6 @@ class PairingTests: TestBase() {
|
||||
}
|
||||
|
||||
fun compare_weights(file1: File, file2: File, skipSeeding: Boolean = false):Boolean {
|
||||
Solver.weightsLogger!!.flush()
|
||||
// Maps to store name pairs and costs
|
||||
val map1 = create_weights_map(file1)
|
||||
val map2 = create_weights_map(file2)
|
||||
@@ -165,6 +162,7 @@ class PairingTests: TestBase() {
|
||||
}
|
||||
|
||||
fun test_from_XML(name: String, forcePairing:List<Int>) {
|
||||
// Let pairgoth use the legacy asymmetric detRandom()
|
||||
test_from_XML_internal(name, forcePairing, true)
|
||||
// Non-legacy tests inhibited for now: pairings differ for Toulouse and SimpleMM
|
||||
// test_from_XML_internal(name, forcePairing, false)
|
||||
@@ -172,11 +170,10 @@ class PairingTests: TestBase() {
|
||||
|
||||
fun test_from_XML_internal(name: String, forcePairing:List<Int>, legacy: Boolean) {
|
||||
// Let pairgoth use the legacy asymmetric detRandom()
|
||||
Solver.legacy_mode = legacy
|
||||
// read tournament with pairing
|
||||
val file = getTestFile("opengotha/pairings/$name.xml")
|
||||
logger.info("read from file $file")
|
||||
val resource = file.readText(StandardCharsets.UTF_8)
|
||||
val tourFile = getTestFile("opengotha/pairings/$name.xml")
|
||||
logger.info("read from file $tourFile")
|
||||
val resource = tourFile.readText(StandardCharsets.UTF_8)
|
||||
var resp = TestAPI.post("/api/tour", resource)
|
||||
val id = resp.asObject().getInt("id")
|
||||
val tournament = TestAPI.get("/api/tour/$id").asObject()
|
||||
@@ -203,15 +200,15 @@ class PairingTests: TestBase() {
|
||||
for (round in 1..tournament.getInt("rounds")!!) {
|
||||
val sumOfWeightsOG = compute_sumOfWeight_OG(getTestFile("opengotha/$name/$name" + "_weights_R$round.txt"), pairingsOG[round-1], players)
|
||||
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
val outputFile = getOutputFile("weights.txt")
|
||||
// Call Pairgoth pairing solver to generate games
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round?legacy=$legacy&weights_output=$outputFile", Json.Array("all")).asArray()
|
||||
logger.info("sumOfWeightOG = " + dec.format(sumOfWeightsOG))
|
||||
logger.info("games for round $round: {}", games.toString())
|
||||
|
||||
// Compare weights with OpenGotha if legacy mode
|
||||
if (legacy) {
|
||||
assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/$name/$name"+"_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_weights(outputFile, getTestFile("opengotha/$name/$name"+"_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
}
|
||||
|
||||
if (round in forcePairing) {
|
||||
@@ -223,7 +220,7 @@ class PairingTests: TestBase() {
|
||||
val gameOG = pairingsOG[round - 1].getJson(i)!!.asObject()// ["r"] as String?
|
||||
val whiteId = gameOG["w"] as Long?
|
||||
val blackId = gameOG["b"] as Long?
|
||||
TestAPI.put("/api/tour/$id/pair/$round", Json.parse("""{"id":$gameID,"w":$whiteId,"b":$blackId}""")).asObject()
|
||||
TestAPI.put("/api/tour/$id/pair/$round?legacy=$legacy&weights_output=$outputFile&append=true", Json.parse("""{"id":$gameID,"w":$whiteId,"b":$blackId}""")).asObject()
|
||||
}
|
||||
games = TestAPI.get("/api/tour/$id/res/$round").asArray()
|
||||
}
|
||||
@@ -273,11 +270,10 @@ class PairingTests: TestBase() {
|
||||
|
||||
@Test
|
||||
fun `SwissTest simpleSwiss`() {
|
||||
Solver.legacy_mode = true
|
||||
// read tournament with pairing
|
||||
var file = getTestFile("opengotha/pairings/simpleswiss.xml")
|
||||
logger.info("read from file $file")
|
||||
val resource = file.readText(StandardCharsets.UTF_8)
|
||||
var tourFile = getTestFile("opengotha/pairings/simpleswiss.xml")
|
||||
logger.info("read from file $tourFile")
|
||||
val resource = tourFile.readText(StandardCharsets.UTF_8)
|
||||
var resp = TestAPI.post("/api/tour", resource)
|
||||
val id = resp.asObject().getInt("id")
|
||||
val tournament = TestAPI.get("/api/tour/$id").asObject()
|
||||
@@ -315,10 +311,10 @@ class PairingTests: TestBase() {
|
||||
var firstGameID: Int
|
||||
|
||||
for (round in 1..7) {
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
val outputFile = getOutputFile("weights.txt")
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round?legacy=true&weights_output=$outputFile", Json.Array("all")).asArray()
|
||||
logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")
|
||||
assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/simpleswiss/simpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_weights(outputFile, getTestFile("opengotha/simpleswiss/simpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_games(games, Json.parse(pairingsOG[round - 1])!!.asArray()),"pairings for round $round differ")
|
||||
logger.info("Pairings for round $round match OpenGotha")
|
||||
|
||||
@@ -354,12 +350,12 @@ class PairingTests: TestBase() {
|
||||
@Test
|
||||
fun `SwissTest KPMCSplitbug`() {
|
||||
// Let pairgoth use the legacy asymmetric detRandom()
|
||||
Solver.legacy_mode = true
|
||||
val legacy = true
|
||||
// read tournament with pairing
|
||||
val name = "20240921-KPMC-Splitbug"
|
||||
val file = getTestFile("opengotha/pairings/$name.xml")
|
||||
logger.info("read from file $file")
|
||||
val resource = file.readText(StandardCharsets.UTF_8)
|
||||
val tourFile = getTestFile("opengotha/pairings/$name.xml")
|
||||
logger.info("read from file $tourFile")
|
||||
val resource = tourFile.readText(StandardCharsets.UTF_8)
|
||||
var resp = TestAPI.post("/api/tour", resource)
|
||||
val id = resp.asObject().getInt("id")
|
||||
val tournament = TestAPI.get("/api/tour/$id").asObject()
|
||||
@@ -387,13 +383,13 @@ class PairingTests: TestBase() {
|
||||
|
||||
var games: Json.Array
|
||||
var firstGameID: Int
|
||||
val outputFile = getOutputFile("weights.txt")
|
||||
|
||||
for (round in minRound..maxRound) {
|
||||
val sumOfWeightsOG = compute_sumOfWeight_OG(getTestFile("opengotha/$name/$name" + "_weights_R$round.txt"), pairingsOG[round - minRound], players)
|
||||
|
||||
Solver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
// Call Pairgoth pairing solver to generate games
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round?legacy=$legacy&weights_ouput=$outputFile&append=${round > 1}", Json.Array("all")).asArray()
|
||||
|
||||
logger.info("sumOfWeightOG = " + dec.format(sumOfWeightsOG))
|
||||
logger.info("games for round $round: {}", games.toString().slice(0..50) + "...")
|
||||
@@ -401,7 +397,7 @@ class PairingTests: TestBase() {
|
||||
// Compare weights with OpenGotha
|
||||
assertTrue(
|
||||
compare_weights(
|
||||
getOutputFile("weights.txt"),
|
||||
outputFile,
|
||||
getTestFile("opengotha/$name/$name" + "_weights_R$round.txt")
|
||||
), "Not matching opengotha weights for round $round"
|
||||
)
|
||||
|
@@ -7,6 +7,8 @@ import org.jeudego.pairgoth.server.SSEServlet
|
||||
import org.jeudego.pairgoth.server.WebappManager
|
||||
import org.mockito.kotlin.*
|
||||
import java.io.*
|
||||
import java.net.URL
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
import javax.servlet.ReadListener
|
||||
@@ -21,20 +23,45 @@ object TestAPI {
|
||||
|
||||
fun Any?.toUnit() = Unit
|
||||
|
||||
fun parseURL(url: String): Pair<String, Map<String, String>> {
|
||||
val qm = url.indexOf('?')
|
||||
if (qm == -1) {
|
||||
return url to emptyMap()
|
||||
}
|
||||
val uri = url.substring(0, qm)
|
||||
val params = url.substring(qm + 1)
|
||||
.split('&')
|
||||
.map { it.split('=') }
|
||||
.mapNotNull {
|
||||
when (it.size) {
|
||||
1 -> it[0].decodeUTF8() to ""
|
||||
2 -> it[0].decodeUTF8() to it[1].decodeUTF8()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.toMap()
|
||||
return uri to params
|
||||
}
|
||||
|
||||
private fun String.decodeUTF8() = URLDecoder.decode(this, "UTF-8") // decode page=%22ABC%22 to page="ABC"
|
||||
|
||||
private val apiServlet = ApiServlet()
|
||||
private val sseServlet = SSEServlet()
|
||||
|
||||
private fun <T> testRequest(reqMethod: String, uri: String, accept: String = "application/json", payload: T? = null): String {
|
||||
private fun <T> testRequest(reqMethod: String, url: String, accept: String = "application/json", payload: T? = null): String {
|
||||
|
||||
WebappManager.properties["auth"] = "none"
|
||||
WebappManager.properties["store"] = "memory"
|
||||
WebappManager.properties["webapp.env"] = "test"
|
||||
|
||||
val (uri, parameters) = parseURL(url)
|
||||
|
||||
// mock request
|
||||
val myHeaderNames = if (reqMethod == "GET") emptyList() else listOf("Content-Type")
|
||||
val selector = argumentCaptor<String>()
|
||||
val subSelector = argumentCaptor<String>()
|
||||
val reqPayload = argumentCaptor<String>()
|
||||
val parameter = argumentCaptor<String>()
|
||||
val myInputStream = payload?.let { DelegatingServletInputStream(payload.toString().byteInputStream(StandardCharsets.UTF_8)) }
|
||||
val myReader = payload?.let { BufferedReader(StringReader(payload.toString())) }
|
||||
val req = mock<HttpServletRequest> {
|
||||
@@ -59,6 +86,7 @@ object TestAPI {
|
||||
}
|
||||
on { headerNames } doReturn Collections.enumeration(myHeaderNames)
|
||||
on { getHeader(eq("Accept")) } doReturn accept
|
||||
on { getParameter(parameter.capture()) } doAnswer { parameters[parameter.lastValue] }
|
||||
}
|
||||
|
||||
// mock response
|
||||
@@ -77,7 +105,7 @@ object TestAPI {
|
||||
"DELETE" -> apiServlet.doDelete(req, resp)
|
||||
}
|
||||
|
||||
return buffer.toString() ?: throw Error("no response payload")
|
||||
return buffer.toString()
|
||||
}
|
||||
|
||||
fun get(uri: String): Json = Json.parse(testRequest<Void>("GET", uri)) ?: throw Error("no payload")
|
||||
|
Reference in New Issue
Block a user