mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-10-26 16:15:51 +01:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec7ea8ea1d
|
||
|
|
05874d6ddb
|
No files matched your search
+38
-36
@@ -55,45 +55,47 @@ class CompactControlsVisualRenderingTest(@param:TempDir private val tempDir: Pat
|
|||||||
}
|
}
|
||||||
val filters = StationFilterState()
|
val filters = StationFilterState()
|
||||||
|
|
||||||
val scene = ImageComposeScene(width = 1366, height = 380, density = Density(scale)) {
|
return onEventThread {
|
||||||
Kst4ContestTheme(darkMode = darkMode, baseFontSizeSp = 12f) {
|
val scene = ImageComposeScene(width = 1366, height = 380, density = Density(scale)) {
|
||||||
Surface(
|
Kst4ContestTheme(darkMode = darkMode, baseFontSizeSp = 12f) {
|
||||||
modifier = Modifier.fillMaxSize(),
|
Surface(
|
||||||
color = MaterialTheme.colorScheme.background,
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
color = MaterialTheme.colorScheme.background,
|
||||||
Column(Modifier.padding(4.dp)) {
|
) {
|
||||||
TimelineView(
|
Column(Modifier.padding(4.dp)) {
|
||||||
state = TimelineState(),
|
TimelineView(
|
||||||
onCandidateClicked = {},
|
state = TimelineState(),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
onCandidateClicked = {},
|
||||||
)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
Spacer(Modifier.height(8.dp))
|
)
|
||||||
StationFilterBar(
|
Spacer(Modifier.height(8.dp))
|
||||||
filters = filters,
|
StationFilterBar(
|
||||||
activeBands = EnumSet.of(Band.B_144, Band.B_432, Band.B_1296),
|
filters = filters,
|
||||||
reachability = ReachabilityState(
|
activeBands = EnumSet.of(Band.B_144, Band.B_432, Band.B_1296),
|
||||||
EnumSet.of(Band.B_144, Band.B_432, Band.B_1296),
|
reachability = ReachabilityState(
|
||||||
),
|
EnumSet.of(Band.B_144, Band.B_432, Band.B_1296),
|
||||||
onChanged = {},
|
),
|
||||||
)
|
onChanged = {},
|
||||||
Spacer(Modifier.height(12.dp))
|
)
|
||||||
ChatInputRow(
|
Spacer(Modifier.height(12.dp))
|
||||||
state = chatInputState,
|
ChatInputRow(
|
||||||
prefs = preferences,
|
state = chatInputState,
|
||||||
ownQrgMain = ObservedValue(preferences.getMYQRGFirstCat()),
|
prefs = preferences,
|
||||||
ownQrgSecond = ObservedValue(preferences.getMYQRGSecondCat()),
|
ownQrgMain = ObservedValue(preferences.getMYQRGFirstCat()),
|
||||||
antennaQtf = ObservedValue(preferences.getActualQTF()),
|
ownQrgSecond = ObservedValue(preferences.getMYQRGSecondCat()),
|
||||||
antennaQtf2 = ObservedValue(preferences.getActualQTF2()),
|
antennaQtf = ObservedValue(preferences.getActualQTF()),
|
||||||
so2rEnabled = false,
|
antennaQtf2 = ObservedValue(preferences.getActualQTF2()),
|
||||||
)
|
so2rEnabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
try {
|
||||||
return try {
|
scene.render().encodeToData()!!.bytes
|
||||||
scene.render().encodeToData()!!.bytes
|
} finally {
|
||||||
} finally {
|
scene.close()
|
||||||
scene.close()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package kst4contest.view.compose
|
||||||
|
|
||||||
|
import java.awt.EventQueue
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An ImageComposeScene must live on the AWT event thread, the thread the application renders
|
||||||
|
* on. Compose Multiplatform 1.8.2 sends the global snapshot notifications there
|
||||||
|
* (GlobalSnapshotManager), and doing so takes two locks in the opposite order from a render
|
||||||
|
* on any other thread:
|
||||||
|
*
|
||||||
|
* - the render holds the scene's FlushCoroutineDispatcher runLock while a task it runs
|
||||||
|
* (here LazyListState.scrollToItem) measures and wants the SnapshotStateObserver's
|
||||||
|
* observedScopeMapsLock;
|
||||||
|
* - the event thread holds observedScopeMapsLock in drainChanges while a derived state it
|
||||||
|
* re-reads wakes a snapshotFlow collector of that scene, whose dispatch wants runLock.
|
||||||
|
*
|
||||||
|
* Both waited forever in CI runs 40252077018, 40252089738 and 40299943329; the thread dump of
|
||||||
|
* the last one shows both stacks. On one thread the two monitors are simply re-entered, which
|
||||||
|
* is why the application never hangs this way.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs [block] on the AWT event thread, waits for it and returns its result. Whatever
|
||||||
|
* [block] throws is rethrown here unwrapped, so a failed assertion still reads as one.
|
||||||
|
*/
|
||||||
|
internal fun <T> onEventThread(block: () -> T): T {
|
||||||
|
if (EventQueue.isDispatchThread()) return block()
|
||||||
|
|
||||||
|
var result: Result<T>? = null
|
||||||
|
try {
|
||||||
|
EventQueue.invokeAndWait { result = runCatching(block) }
|
||||||
|
} catch (wrapped: InvocationTargetException) {
|
||||||
|
throw wrapped.cause ?: wrapped
|
||||||
|
}
|
||||||
|
return result!!.getOrThrow()
|
||||||
|
}
|
||||||
+29
-27
@@ -84,37 +84,39 @@ class SelectedStationMessageUiVisualRenderingTest {
|
|||||||
replaceRows(listOf(message(member("DL1ABC", 2), member("DL2XYZ", 2), "pse sked")))
|
replaceRows(listOf(message(member("DL1ABC", 2), member("DL2XYZ", 2), "pse sked")))
|
||||||
}
|
}
|
||||||
|
|
||||||
val scene = ImageComposeScene(width = 1366, height = windowHeight, density = Density(scale)) {
|
return onEventThread {
|
||||||
Kst4ContestTheme(darkMode = darkMode, baseFontSizeSp = 12f) {
|
val scene = ImageComposeScene(width = 1366, height = windowHeight, density = Density(scale)) {
|
||||||
Surface(
|
Kst4ContestTheme(darkMode = darkMode, baseFontSizeSp = 12f) {
|
||||||
modifier = Modifier.fillMaxSize(),
|
Surface(
|
||||||
color = MaterialTheme.colorScheme.background,
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
color = MaterialTheme.colorScheme.background,
|
||||||
Row(Modifier.fillMaxSize().padding(6.dp)) {
|
) {
|
||||||
MessageTabs(
|
Row(Modifier.fillMaxSize().padding(6.dp)) {
|
||||||
publicMessages = publicMessages,
|
MessageTabs(
|
||||||
clusterMessages = clusterTable(),
|
publicMessages = publicMessages,
|
||||||
qsoOfTheOther = qsoMessages,
|
clusterMessages = clusterTable(),
|
||||||
modifier = Modifier.weight(1f),
|
qsoOfTheOther = qsoMessages,
|
||||||
)
|
modifier = Modifier.weight(1f),
|
||||||
SelectedStationPanel(
|
)
|
||||||
state = selectedState,
|
SelectedStationPanel(
|
||||||
messages = selectedMessages,
|
state = selectedState,
|
||||||
activeBands = EnumSet.of(Band.B_144, Band.B_432, Band.B_1296),
|
messages = selectedMessages,
|
||||||
openInBrowser = Consumer {},
|
activeBands = EnumSet.of(Band.B_144, Band.B_432, Band.B_1296),
|
||||||
onShowOnMap = {},
|
openInBrowser = Consumer {},
|
||||||
onShowPathInAirScout = {},
|
onShowOnMap = {},
|
||||||
modifier = Modifier.width(panelWidth).padding(start = 6.dp),
|
onShowPathInAirScout = {},
|
||||||
)
|
modifier = Modifier.width(panelWidth).padding(start = 6.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
try {
|
||||||
scene.render().encodeToData()!!.bytes
|
scene.render().encodeToData()!!.bytes
|
||||||
} finally {
|
} finally {
|
||||||
scene.close()
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+53
-51
@@ -87,62 +87,64 @@ class StationWorkspaceVisualRenderingTest {
|
|||||||
RowKeys.byReference<ChatMessage>(),
|
RowKeys.byReference<ChatMessage>(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val scene = ImageComposeScene(
|
return onEventThread {
|
||||||
width = 1366,
|
val scene = ImageComposeScene(
|
||||||
height = scenario.height,
|
width = 1366,
|
||||||
density = Density(scale),
|
height = scenario.height,
|
||||||
) {
|
density = Density(scale),
|
||||||
Kst4ContestTheme(darkMode = darkMode, baseFontSizeSp = 12f) {
|
) {
|
||||||
Surface(
|
Kst4ContestTheme(darkMode = darkMode, baseFontSizeSp = 12f) {
|
||||||
modifier = Modifier.fillMaxSize(),
|
Surface(
|
||||||
color = MaterialTheme.colorScheme.background,
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
color = MaterialTheme.colorScheme.background,
|
||||||
Box(Modifier.fillMaxSize()) {
|
|
||||||
Column(
|
|
||||||
Modifier
|
|
||||||
.width(scenario.width.dp)
|
|
||||||
.fillMaxHeight()
|
|
||||||
.align(Alignment.TopEnd)
|
|
||||||
.padding(4.dp),
|
|
||||||
) {
|
) {
|
||||||
StationFilterBar(
|
Box(Modifier.fillMaxSize()) {
|
||||||
filters = StationFilterState(),
|
Column(
|
||||||
activeBands = EnumSet.of(Band.B_144, Band.B_432),
|
Modifier
|
||||||
reachability = ReachabilityState(EnumSet.of(Band.B_144, Band.B_432)),
|
.width(scenario.width.dp)
|
||||||
onChanged = {},
|
.fillMaxHeight()
|
||||||
)
|
.align(Alignment.TopEnd)
|
||||||
DataTableView(
|
.padding(4.dp),
|
||||||
state = stations,
|
) {
|
||||||
modifier = Modifier.weight(0.45f),
|
StationFilterBar(
|
||||||
emphasizeCell = { member, column ->
|
filters = StationFilterState(),
|
||||||
column.id == "callsign" && newJoined.isRecent(member)
|
activeBands = EnumSet.of(Band.B_144, Band.B_432),
|
||||||
},
|
reachability = ReachabilityState(EnumSet.of(Band.B_144, Band.B_432)),
|
||||||
)
|
onChanged = {},
|
||||||
TopPriorityBar(
|
)
|
||||||
state = priority,
|
DataTableView(
|
||||||
newJoined = newJoined,
|
state = stations,
|
||||||
onShowMore = {},
|
modifier = Modifier.weight(0.45f),
|
||||||
)
|
emphasizeCell = { member, column ->
|
||||||
SelectedStationPanel(
|
column.id == "callsign" && newJoined.isRecent(member)
|
||||||
state = selected,
|
},
|
||||||
messages = selectedMessages,
|
)
|
||||||
activeBands = EnumSet.of(Band.B_144, Band.B_432),
|
TopPriorityBar(
|
||||||
contentSplitter = SplitterState(2, doubleArrayOf(0.48)) {},
|
state = priority,
|
||||||
openInBrowser = Consumer {},
|
newJoined = newJoined,
|
||||||
onShowOnMap = {},
|
onShowMore = {},
|
||||||
onShowPathInAirScout = {},
|
)
|
||||||
modifier = Modifier.weight(0.55f),
|
SelectedStationPanel(
|
||||||
)
|
state = selected,
|
||||||
}
|
messages = selectedMessages,
|
||||||
|
activeBands = EnumSet.of(Band.B_144, Band.B_432),
|
||||||
|
contentSplitter = SplitterState(2, doubleArrayOf(0.48)) {},
|
||||||
|
openInBrowser = Consumer {},
|
||||||
|
onShowOnMap = {},
|
||||||
|
onShowPathInAirScout = {},
|
||||||
|
modifier = Modifier.weight(0.55f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return try {
|
try {
|
||||||
scene.render().encodeToData()!!.bytes
|
scene.render().encodeToData()!!.bytes
|
||||||
} finally {
|
} finally {
|
||||||
scene.close()
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-17
@@ -32,17 +32,19 @@ class TopPriorityCandidatesWindowTest {
|
|||||||
/** Runs [block] against a rendered list of [twoCandidates] and reports what it picked. */
|
/** Runs [block] against a rendered list of [twoCandidates] and reports what it picked. */
|
||||||
private fun picking(block: (ImageComposeScene) -> Unit): String? {
|
private fun picking(block: (ImageComposeScene) -> Unit): String? {
|
||||||
var picked: String? = null
|
var picked: String? = null
|
||||||
val scene = ImageComposeScene(width = 360, height = 500, density = Density(1f)) {
|
onEventThread {
|
||||||
TopPriorityCandidatesWindow.CandidateListForTest(twoCandidates) {
|
val scene = ImageComposeScene(width = 360, height = 500, density = Density(1f)) {
|
||||||
picked = it.displayCallSign
|
TopPriorityCandidatesWindow.CandidateListForTest(twoCandidates) {
|
||||||
|
picked = it.displayCallSign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
scene.render()
|
||||||
|
block(scene)
|
||||||
|
scene.render()
|
||||||
|
} finally {
|
||||||
|
scene.close()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
try {
|
|
||||||
scene.render()
|
|
||||||
block(scene)
|
|
||||||
scene.render()
|
|
||||||
} finally {
|
|
||||||
scene.close()
|
|
||||||
}
|
}
|
||||||
return picked
|
return picked
|
||||||
}
|
}
|
||||||
@@ -78,13 +80,15 @@ class TopPriorityCandidatesWindowTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `the list renders with candidates and without`() {
|
fun `the list renders with candidates and without`() {
|
||||||
listOf(emptyList(), twoCandidates).forEach { ranking ->
|
listOf(emptyList(), twoCandidates).forEach { ranking ->
|
||||||
val scene = ImageComposeScene(width = 360, height = 500, density = Density(1f)) {
|
onEventThread {
|
||||||
TopPriorityCandidatesWindow.CandidateListForTest(ranking) { }
|
val scene = ImageComposeScene(width = 360, height = 500, density = Density(1f)) {
|
||||||
}
|
TopPriorityCandidatesWindow.CandidateListForTest(ranking) { }
|
||||||
try {
|
}
|
||||||
scene.render()
|
try {
|
||||||
} finally {
|
scene.render()
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-21
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.ui.ImageComposeScene
|
import androidx.compose.ui.ImageComposeScene
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
|
import kst4contest.view.compose.onEventThread
|
||||||
import kst4contest.view.map.MapCallsignRawSnapshot
|
import kst4contest.view.map.MapCallsignRawSnapshot
|
||||||
import kst4contest.view.map.MapViewport
|
import kst4contest.view.map.MapViewport
|
||||||
import kst4contest.view.map.TileFetcher
|
import kst4contest.view.map.TileFetcher
|
||||||
@@ -71,27 +72,29 @@ class ComposeStationMapDrawingTest {
|
|||||||
workedGrossFields: Set<String> = emptySet(),
|
workedGrossFields: Set<String> = emptySet(),
|
||||||
darkMode: Boolean = true,
|
darkMode: Boolean = true,
|
||||||
): BufferedImage {
|
): BufferedImage {
|
||||||
val scene = ImageComposeScene(canvasWidth, canvasHeight, Density(1f)) {
|
return onEventThread {
|
||||||
ComposeStationMap(
|
val scene = ImageComposeScene(canvasWidth, canvasHeight, Density(1f)) {
|
||||||
markers = markers,
|
ComposeStationMap(
|
||||||
tileFetcher = offlineTileFetcher,
|
markers = markers,
|
||||||
mapState = MapState(zoom = zoom, centerLon = 10.0, centerLat = 51.0),
|
tileFetcher = offlineTileFetcher,
|
||||||
groupingEnabled = false,
|
mapState = MapState(zoom = zoom, centerLon = 10.0, centerLat = 51.0),
|
||||||
selectedMarkerCallsign = null,
|
groupingEnabled = false,
|
||||||
ownLocator6 = ownLocator6,
|
selectedMarkerCallsign = null,
|
||||||
antennaAzimuthDeg = 90.0,
|
ownLocator6 = ownLocator6,
|
||||||
antennaBeamWidthDeg = 0.0,
|
antennaAzimuthDeg = 90.0,
|
||||||
maxQrbKm = 0.0,
|
antennaBeamWidthDeg = 0.0,
|
||||||
onMarkerClicked = null,
|
maxQrbKm = 0.0,
|
||||||
workedGrossFields = workedGrossFields,
|
onMarkerClicked = null,
|
||||||
darkMode = darkMode,
|
workedGrossFields = workedGrossFields,
|
||||||
modifier = Modifier.fillMaxSize(),
|
darkMode = darkMode,
|
||||||
)
|
modifier = Modifier.fillMaxSize(),
|
||||||
}
|
)
|
||||||
try {
|
}
|
||||||
return ImageIO.read(ByteArrayInputStream(scene.render().encodeToData()!!.bytes))
|
try {
|
||||||
} finally {
|
ImageIO.read(ByteArrayInputStream(scene.render().encodeToData()!!.bytes))
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-24
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.ui.ImageComposeScene
|
import androidx.compose.ui.ImageComposeScene
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
|
import kst4contest.view.compose.onEventThread
|
||||||
import kst4contest.view.map.MapCallsignRawSnapshot
|
import kst4contest.view.map.MapCallsignRawSnapshot
|
||||||
import kst4contest.view.map.TileFetcher
|
import kst4contest.view.map.TileFetcher
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -33,30 +34,32 @@ class ComposeStationMapLayoutTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
private fun renderAt(widthPx: Int, heightPx: Int, zoom: Float) {
|
private fun renderAt(widthPx: Int, heightPx: Int, zoom: Float) {
|
||||||
val scene = ImageComposeScene(
|
onEventThread {
|
||||||
width = widthPx,
|
val scene = ImageComposeScene(
|
||||||
height = heightPx,
|
width = widthPx,
|
||||||
density = Density(1f),
|
height = heightPx,
|
||||||
) {
|
density = Density(1f),
|
||||||
ComposeStationMap(
|
) {
|
||||||
markers = stations,
|
ComposeStationMap(
|
||||||
tileFetcher = TileFetcher(),
|
markers = stations,
|
||||||
mapState = MapState(zoom = zoom, centerLon = 10.0, centerLat = 51.0),
|
tileFetcher = TileFetcher(),
|
||||||
groupingEnabled = true,
|
mapState = MapState(zoom = zoom, centerLon = 10.0, centerLat = 51.0),
|
||||||
selectedMarkerCallsign = "F5JMI",
|
groupingEnabled = true,
|
||||||
ownLocator6 = "JO50JP",
|
selectedMarkerCallsign = "F5JMI",
|
||||||
antennaAzimuthDeg = 229.0,
|
ownLocator6 = "JO50JP",
|
||||||
antennaBeamWidthDeg = 60.0,
|
antennaAzimuthDeg = 229.0,
|
||||||
maxQrbKm = 800.0,
|
antennaBeamWidthDeg = 60.0,
|
||||||
onMarkerClicked = null,
|
maxQrbKm = 800.0,
|
||||||
darkMode = true,
|
onMarkerClicked = null,
|
||||||
modifier = Modifier.fillMaxSize(),
|
darkMode = true,
|
||||||
)
|
modifier = Modifier.fillMaxSize(),
|
||||||
}
|
)
|
||||||
try {
|
}
|
||||||
scene.render()
|
try {
|
||||||
} finally {
|
scene.render()
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-15
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.ui.ImageComposeScene
|
import androidx.compose.ui.ImageComposeScene
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
|
import kst4contest.view.compose.onEventThread
|
||||||
import kst4contest.view.map.PathAnalysisResult
|
import kst4contest.view.map.PathAnalysisResult
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
@@ -25,21 +26,23 @@ class PathAnalysisDetailsLayoutTest {
|
|||||||
private val narrowWidthPx = 120
|
private val narrowWidthPx = 120
|
||||||
|
|
||||||
private fun renderAt(widthPx: Int, result: PathAnalysisResult?) {
|
private fun renderAt(widthPx: Int, result: PathAnalysisResult?) {
|
||||||
val scene = ImageComposeScene(
|
onEventThread {
|
||||||
width = widthPx,
|
val scene = ImageComposeScene(
|
||||||
height = 600,
|
width = widthPx,
|
||||||
density = Density(1f),
|
height = 600,
|
||||||
) {
|
density = Density(1f),
|
||||||
PathAnalysisDetails(
|
) {
|
||||||
result = result,
|
PathAnalysisDetails(
|
||||||
darkMode = true,
|
result = result,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
darkMode = true,
|
||||||
)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
}
|
)
|
||||||
try {
|
}
|
||||||
scene.render()
|
try {
|
||||||
} finally {
|
scene.render()
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-12
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.ui.ImageComposeScene
|
import androidx.compose.ui.ImageComposeScene
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
|
import kst4contest.view.compose.onEventThread
|
||||||
import kst4contest.view.map.PathAnalysisResult
|
import kst4contest.view.map.PathAnalysisResult
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -31,18 +32,20 @@ class PathAnalysisDetailsWidthTest {
|
|||||||
private val assessmentBackground = 0x33383E
|
private val assessmentBackground = 0x33383E
|
||||||
|
|
||||||
private fun render(paneWidthPx: Int, heightPx: Int = 600): BufferedImage {
|
private fun render(paneWidthPx: Int, heightPx: Int = 600): BufferedImage {
|
||||||
val scene = ImageComposeScene(width = paneWidthPx, height = heightPx, density = Density(1f)) {
|
return onEventThread {
|
||||||
PathAnalysisDetails(
|
val scene = ImageComposeScene(width = paneWidthPx, height = heightPx, density = Density(1f)) {
|
||||||
result = PathAnalysisResult.loading("JO50JP", "JN24JB", "F5JMI"),
|
PathAnalysisDetails(
|
||||||
darkMode = true,
|
result = PathAnalysisResult.loading("JO50JP", "JN24JB", "F5JMI"),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
darkMode = true,
|
||||||
)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
}
|
)
|
||||||
try {
|
}
|
||||||
val png = scene.render().encodeToData()!!.bytes
|
try {
|
||||||
return ImageIO.read(ByteArrayInputStream(png))
|
val png = scene.render().encodeToData()!!.bytes
|
||||||
} finally {
|
ImageIO.read(ByteArrayInputStream(png))
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-11
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.ui.ImageComposeScene
|
import androidx.compose.ui.ImageComposeScene
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
|
import kst4contest.view.compose.onEventThread
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,17 +19,19 @@ import org.junit.jupiter.api.Test
|
|||||||
class PathProfileChartLayoutTest {
|
class PathProfileChartLayoutTest {
|
||||||
|
|
||||||
private fun renderAt(widthPx: Int, heightPx: Int) {
|
private fun renderAt(widthPx: Int, heightPx: Int) {
|
||||||
val scene = ImageComposeScene(
|
onEventThread {
|
||||||
width = widthPx,
|
val scene = ImageComposeScene(
|
||||||
height = heightPx,
|
width = widthPx,
|
||||||
density = Density(1f),
|
height = heightPx,
|
||||||
) {
|
density = Density(1f),
|
||||||
PathProfileChart(modifier = Modifier.fillMaxSize(), darkMode = true)
|
) {
|
||||||
}
|
PathProfileChart(modifier = Modifier.fillMaxSize(), darkMode = true)
|
||||||
try {
|
}
|
||||||
scene.render()
|
try {
|
||||||
} finally {
|
scene.render()
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -15,6 +15,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import kst4contest.view.compose.SplitterOrientation
|
import kst4contest.view.compose.SplitterOrientation
|
||||||
import kst4contest.view.compose.SplitterPane
|
import kst4contest.view.compose.SplitterPane
|
||||||
import kst4contest.view.compose.SplitterState
|
import kst4contest.view.compose.SplitterState
|
||||||
|
import kst4contest.view.compose.onEventThread
|
||||||
import kst4contest.view.map.MapCallsignRawSnapshot
|
import kst4contest.view.map.MapCallsignRawSnapshot
|
||||||
import kst4contest.view.map.PathAnalysisResult
|
import kst4contest.view.map.PathAnalysisResult
|
||||||
import kst4contest.view.map.TileFetcher
|
import kst4contest.view.map.TileFetcher
|
||||||
@@ -88,13 +89,15 @@ class StationMapBodyLayoutTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderAt(widthPx: Int, heightPx: Int, pathAnalysisVisible: Boolean = true) {
|
private fun renderAt(widthPx: Int, heightPx: Int, pathAnalysisVisible: Boolean = true) {
|
||||||
val scene = ImageComposeScene(width = widthPx, height = heightPx, density = Density(1f)) {
|
onEventThread {
|
||||||
Body(pathAnalysisVisible)
|
val scene = ImageComposeScene(width = widthPx, height = heightPx, density = Density(1f)) {
|
||||||
}
|
Body(pathAnalysisVisible)
|
||||||
try {
|
}
|
||||||
scene.render()
|
try {
|
||||||
} finally {
|
scene.render()
|
||||||
scene.close()
|
} finally {
|
||||||
|
scene.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,13 @@ subprojects {
|
|||||||
*/
|
*/
|
||||||
systemProperty("junit.jupiter.execution.timeout.default", "2m")
|
systemProperty("junit.jupiter.execution.timeout.default", "2m")
|
||||||
systemProperty("junit.jupiter.execution.timeout.thread.mode.default", "SEPARATE_THREAD")
|
systemProperty("junit.jupiter.execution.timeout.thread.mode.default", "SEPARATE_THREAD")
|
||||||
|
/*
|
||||||
|
* The timed-out thread's stack shows where a test waits, not who it waits for: the
|
||||||
|
* rare CI hang in SelectedStationMessageUiVisualRenderingTest blocks on a monitor
|
||||||
|
* that another thread holds. On a timeout JUnit prints all threads to the test's
|
||||||
|
* standard output, which lands in its XML report (the test-results artifact).
|
||||||
|
*/
|
||||||
|
systemProperty("junit.jupiter.execution.timeout.threaddump.enabled", "true")
|
||||||
// The default short format prints only the first frame of each cause, which for a
|
// The default short format prints only the first frame of each cause, which for a
|
||||||
// timed-out test is the line it hung in but not the call that blocked there.
|
// timed-out test is the line it hung in but not the call that blocked there.
|
||||||
testLogging {
|
testLogging {
|
||||||
|
|||||||
Reference in new issue
Block a user