forked from Hexworks/zircon
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes Hexworks#386.
- Loading branch information
Showing
10 changed files
with
265 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
zircon.core/src/commonMain/kotlin/org/hexworks/zircon/api/tileset/ChainedTilesetLoader.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package org.hexworks.zircon.api.tileset | ||
|
||
import org.hexworks.zircon.api.resource.TilesetResource | ||
|
||
/** | ||
* A higher-order [TilesetLoader] that will try to load a resource using [loaderA], and if it can't, tries [loaderB]. | ||
* If neither can load the resource, an exception is thrown. | ||
* | ||
* Whether a [TilesetLoader] is able to load a [TilesetResource] is dependent on [TilesetLoader.canLoadResource]. | ||
*/ | ||
class ChainedTilesetLoader<T : Any>( | ||
private val loaderA: TilesetLoader<T>, | ||
private val loaderB: TilesetLoader<T> | ||
) : TilesetLoader<T> { | ||
override fun loadTilesetFrom(resource: TilesetResource): Tileset<T> = | ||
when { | ||
loaderA.canLoadResource(resource) -> loaderA.loadTilesetFrom(resource) | ||
loaderB.canLoadResource(resource) -> loaderB.loadTilesetFrom(resource) | ||
else -> throw IllegalArgumentException("Unknown tile type '${resource.tileType}'.") | ||
} | ||
|
||
override fun canLoadResource(resource: TilesetResource): Boolean = | ||
loaderA.canLoadResource(resource) || loaderB.canLoadResource(resource) | ||
|
||
companion object { | ||
/** | ||
* Constructs a new [TilesetLoader] that will check each [TilesetLoader] given, in order, until it finds one | ||
* that can load the given resource. | ||
*/ | ||
fun <T : Any> inOrder(tilesetLoaders: List<TilesetLoader<T>>): TilesetLoader<T> { | ||
return when (tilesetLoaders.size) { | ||
0 -> throw IllegalArgumentException("tilesetLoaders cannot be empty") | ||
1 -> tilesetLoaders.first() | ||
else -> tilesetLoaders.reduceRight { right: TilesetLoader<T>, acc: TilesetLoader<T> -> ChainedTilesetLoader(right, acc) } | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
zircon.core/src/jvmTest/kotlin/org/hexworks/zircon/api/tileset/ChainedTilesetLoaderTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package org.hexworks.zircon.api.tileset | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.assertj.core.api.Assertions.assertThatThrownBy | ||
import org.hexworks.zircon.api.resource.TilesetResource | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.mockito.Mock | ||
import org.mockito.junit.MockitoJUnit | ||
import org.mockito.junit.MockitoRule | ||
import org.mockito.kotlin.* | ||
import org.mockito.quality.Strictness | ||
|
||
/* | ||
This is a very rigid set of test cases. Normally this is an anti-pattern, but we want to verify very specific behavior | ||
here. Exactly these methods are called in exactly this particular order. | ||
*/ | ||
class ChainedTilesetLoaderTest { | ||
@get:Rule | ||
val mockitoRule: MockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS) | ||
|
||
@Mock(name="loaderA") | ||
lateinit var loaderA: TilesetLoader<Any> | ||
@Mock(name="loaderB") | ||
lateinit var loaderB: TilesetLoader<Any> | ||
@Mock(name="loaderC") | ||
lateinit var loaderC: TilesetLoader<Any> | ||
|
||
@Test | ||
fun firstLoaderCanLoad() { | ||
val mockTilesetResource = mock<TilesetResource>() | ||
val mockTileset = mock<Tileset<Any>>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(true) | ||
whenever(loaderA.loadTilesetFrom(any())).thenReturn(mockTileset) | ||
|
||
ChainedTilesetLoader(loaderA, loaderB).also { chained -> | ||
assertThat(chained.loadTilesetFrom(mockTilesetResource)).isSameAs(mockTileset) | ||
} | ||
|
||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderA).loadTilesetFrom(mockTilesetResource) | ||
} | ||
|
||
@Test | ||
fun secondLoaderCanLoad() { | ||
val mockTilesetResource = mock<TilesetResource>() | ||
val mockTileset = mock<Tileset<Any>>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderB.canLoadResource(any())).thenReturn(true) | ||
whenever(loaderB.loadTilesetFrom(any())).thenReturn(mockTileset) | ||
|
||
ChainedTilesetLoader(loaderA, loaderB).also { chained -> | ||
assertThat(chained.loadTilesetFrom(mockTilesetResource)).isSameAs(mockTileset) | ||
} | ||
|
||
inOrder(loaderA, loaderB) { | ||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderB).canLoadResource(mockTilesetResource) | ||
verify(loaderB).loadTilesetFrom(mockTilesetResource) | ||
} | ||
} | ||
|
||
@Test | ||
fun neitherCanLoad() { | ||
val mockTilesetResource = mock<TilesetResource>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderB.canLoadResource(any())).thenReturn(false) | ||
|
||
ChainedTilesetLoader(loaderA, loaderB).also { chained -> | ||
assertThatThrownBy { | ||
chained.loadTilesetFrom(mockTilesetResource) | ||
} | ||
.isInstanceOf(IllegalArgumentException::class.java) | ||
.hasMessageContaining("Unknown tile type") | ||
} | ||
|
||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderB).canLoadResource(mockTilesetResource) | ||
} | ||
|
||
@Test | ||
fun canLoadResourceA() { | ||
val mockTilesetResource = mock<TilesetResource>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(true) | ||
|
||
ChainedTilesetLoader(loaderA, loaderB).also { chained -> | ||
assertThat(chained.canLoadResource(mockTilesetResource)).isTrue() | ||
} | ||
|
||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
} | ||
|
||
@Test | ||
fun canLoadResourceB() { | ||
val mockTilesetResource = mock<TilesetResource>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderB.canLoadResource(any())).thenReturn(true) | ||
|
||
ChainedTilesetLoader(loaderA, loaderB).also { chained -> | ||
assertThat(chained.canLoadResource(mockTilesetResource)).isTrue() | ||
} | ||
|
||
inOrder(loaderA, loaderB) { | ||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderB).canLoadResource(mockTilesetResource) | ||
} | ||
} | ||
|
||
@Test | ||
fun canLoadResourceC() { | ||
val mockTilesetResource = mock<TilesetResource>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderB.canLoadResource(any())).thenReturn(false) | ||
|
||
ChainedTilesetLoader(loaderA, loaderB).also { chained -> | ||
assertThat(chained.canLoadResource(mockTilesetResource)).isFalse() | ||
} | ||
|
||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderB).canLoadResource(mockTilesetResource) | ||
} | ||
|
||
@Test | ||
fun inOrder_empty() { | ||
assertThatThrownBy { | ||
ChainedTilesetLoader.inOrder(emptyList<TilesetLoader<Any>>()) | ||
} | ||
.isInstanceOf(IllegalArgumentException::class.java) | ||
.hasMessageContaining("cannot be empty") | ||
} | ||
|
||
@Test | ||
fun inOrder_single() { | ||
// This is a little odd, but really why have chaining at all if you only have a single loader anyway? | ||
assertThat(ChainedTilesetLoader.inOrder(listOf(loaderA))) | ||
.isSameAs(loaderA) | ||
} | ||
|
||
@Test | ||
fun inOrder_double() { | ||
val chained = ChainedTilesetLoader.inOrder(listOf(loaderA, loaderB)) | ||
|
||
val mockTilesetResource = mock<TilesetResource>() | ||
val mockTileset = mock<Tileset<Any>>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderB.canLoadResource(any())).thenReturn(true) | ||
whenever(loaderB.loadTilesetFrom(any())).thenReturn(mockTileset) | ||
|
||
assertThat(chained.loadTilesetFrom(mockTilesetResource)).isSameAs(mockTileset) | ||
|
||
inOrder(loaderA, loaderB) { | ||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderB).canLoadResource(mockTilesetResource) | ||
verify(loaderB).loadTilesetFrom(mockTilesetResource) | ||
} | ||
} | ||
|
||
@Test | ||
fun inOrder_triple() { | ||
val chained = ChainedTilesetLoader.inOrder(listOf(loaderA, loaderB, loaderC)) | ||
|
||
val mockTilesetResource = mock<TilesetResource>() | ||
val mockTileset = mock<Tileset<Any>>() | ||
whenever(loaderA.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderB.canLoadResource(any())).thenReturn(false) | ||
whenever(loaderC.canLoadResource(any())).thenReturn(true) | ||
whenever(loaderC.loadTilesetFrom(any())).thenReturn(mockTileset) | ||
|
||
assertThat(chained.loadTilesetFrom(mockTilesetResource)).isSameAs(mockTileset) | ||
|
||
inOrder(loaderA, loaderB, loaderC) { | ||
verify(loaderA).canLoadResource(mockTilesetResource) | ||
verify(loaderB).canLoadResource(mockTilesetResource) | ||
verify(loaderC).canLoadResource(mockTilesetResource) | ||
verify(loaderC).loadTilesetFrom(mockTilesetResource) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters