mirror of
https://github.com/corda/corda.git
synced 2025-02-20 17:33:15 +00:00
client, explorer: Address review comments
This commit is contained in:
parent
9a212a8714
commit
4da73e28c7
@ -1,19 +1,46 @@
|
||||
package com.r3corda.client.fxutils
|
||||
|
||||
import co.paralleluniverse.common.util.VisibleForTesting
|
||||
import javafx.collections.ListChangeListener
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.collections.transformation.TransformationList
|
||||
import java.util.*
|
||||
import kotlin.comparisons.compareValues
|
||||
|
||||
/**
|
||||
* [ConcatenatedList] takes a list of lists and concatenates them. Any change to the underlying lists or the outer list
|
||||
* is propagated as expected.
|
||||
*/
|
||||
class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : TransformationList<A, ObservableList<A>>(sourceList) {
|
||||
class WrappedObservableList<A>(
|
||||
// A wrapper for input lists so we hash differently even if a list is reused in the input.
|
||||
@VisibleForTesting
|
||||
internal class WrappedObservableList<A>(
|
||||
val observableList: ObservableList<A>
|
||||
)
|
||||
private val indexMap = HashMap<WrappedObservableList<out A>, Pair<Int, ListChangeListener<A>>>()
|
||||
// Maps each list index to the offset of the next nested element
|
||||
// Example: { {"a", "b"}, {"c"} } -> { 2, 3 }
|
||||
private val nestedIndexOffsets = ArrayList<Int>(sourceList.size)
|
||||
// First let's clarify some concepts as it's easy to confuse which list we're handling where.
|
||||
// Throughout the commentary and the code we will refer to the lists contained in the source list as "nested lists",
|
||||
// their elements "nested elements", whereas the containing list will be called "source list", its elements being
|
||||
// the nested lists. We will refer to the final concatenated list as "result list".
|
||||
|
||||
// We maintain two bookkeeping data-structures.
|
||||
// 'indexMap' stores a mapping from nested lists to their respective source list indices and listeners.
|
||||
// 'nestedIndexOffsets' stores for each nested list the index of the *next* nested element in the result list.
|
||||
// We also have a helper function 'startingOffsetOf', which given an index of a nested list in the source list
|
||||
// returns the index of its first element in the result list, or where it would be if it had one.
|
||||
// For example:
|
||||
// nested lists = { {"a", "b"}, {"c"}, {} }
|
||||
// result list = { "a", "b", "c" }
|
||||
// indexMap = [ {"c"} -> (1, listener),
|
||||
// {} -> (2, listener),
|
||||
// {"a", "b"} -> (0, listener) ]
|
||||
// nestedIndexOffsets = { 2, 3, 3 }
|
||||
// startingOffsetOf = { 0, 2, 3 }
|
||||
// Note that similar to 'nestedIndexOffsets', 'startingOffsetOf' also isn't a one-to-one mapping because of
|
||||
// potentially several empty nested lists.
|
||||
@VisibleForTesting
|
||||
internal val indexMap = HashMap<WrappedObservableList<out A>, Pair<Int, ListChangeListener<A>>>()
|
||||
@VisibleForTesting
|
||||
internal val nestedIndexOffsets = ArrayList<Int>(sourceList.size)
|
||||
init {
|
||||
var offset = 0
|
||||
sourceList.forEachIndexed { index, observableList ->
|
||||
@ -24,26 +51,40 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
}
|
||||
}
|
||||
|
||||
private fun startingOffsetOf(listIndex: Int): Int {
|
||||
if (listIndex == 0) {
|
||||
return 0
|
||||
} else {
|
||||
return nestedIndexOffsets[listIndex - 1]
|
||||
}
|
||||
}
|
||||
|
||||
// This is where we create a listener for a *nested* list. Note that 'indexMap' doesn't need to be adjusted on any
|
||||
// of these changes as the indices of nested lists don't change, just their contents.
|
||||
private fun createListener(wrapped: WrappedObservableList<A>): ListChangeListener<A> {
|
||||
val listener = ListChangeListener<A> { change ->
|
||||
beginChange()
|
||||
while (change.next()) {
|
||||
if (change.wasPermutated()) {
|
||||
val listIndex = indexMap[wrapped]!!.first
|
||||
// If a nested list is permuted we simply offset the permutation by the startingOffsetOf the list.
|
||||
// Note that we don't need to invalidate offsets.
|
||||
val nestedListIndex = indexMap[wrapped]!!.first
|
||||
val permutation = IntArray(change.to)
|
||||
if (listIndex >= firstInvalidatedPosition) {
|
||||
recalculateOffsets()
|
||||
}
|
||||
val startingOffset = startingOffsetOf(listIndex)
|
||||
val startingOffset = startingOffsetOf(nestedListIndex)
|
||||
// firstTouched is the result list index of the beginning of the permutation.
|
||||
val firstTouched = startingOffset + change.from
|
||||
// We first set the non-permuted indices.
|
||||
for (i in 0..firstTouched - 1) {
|
||||
permutation[i] = i
|
||||
}
|
||||
for (i in startingOffset + change.from..startingOffset + change.to - 1) {
|
||||
// Then the permuted ones.
|
||||
for (i in firstTouched .. startingOffset + change.to - 1) {
|
||||
permutation[startingOffset + i] = change.getPermutation(i)
|
||||
}
|
||||
nextPermutation(firstTouched, startingOffset + change.to, permutation)
|
||||
} else if (change.wasUpdated()) {
|
||||
// If a nested element is updated we simply propagate the update by offsetting the nested element index
|
||||
// by the startingOffsetOf the nested list.
|
||||
val listIndex = indexMap[wrapped]!!.first
|
||||
val startingOffset = startingOffsetOf(listIndex)
|
||||
for (i in change.from..change.to - 1) {
|
||||
@ -51,14 +92,21 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
}
|
||||
} else {
|
||||
if (change.wasRemoved()) {
|
||||
// If nested elements are removed we again simply offset the change. We also need to invalidate
|
||||
// 'nestedIndexOffsets' unless we removed the same number of elements as we added
|
||||
val listIndex = indexMap[wrapped]!!.first
|
||||
invalidateOffsets(listIndex)
|
||||
if (!(change.wasAdded() && change.addedSize == change.removedSize)) {
|
||||
invalidateOffsets(listIndex)
|
||||
}
|
||||
val startingOffset = startingOffsetOf(listIndex)
|
||||
nextRemove(startingOffset + change.from, change.removed)
|
||||
}
|
||||
if (change.wasAdded()) {
|
||||
// Similar logic to remove.
|
||||
val listIndex = indexMap[wrapped]!!.first
|
||||
invalidateOffsets(listIndex)
|
||||
if (!(change.wasRemoved() && change.addedSize == change.removedSize)) {
|
||||
invalidateOffsets(listIndex)
|
||||
}
|
||||
val startingOffset = startingOffsetOf(listIndex)
|
||||
nextAdd(startingOffset + change.from, startingOffset + change.to)
|
||||
}
|
||||
@ -71,23 +119,28 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
return listener
|
||||
}
|
||||
|
||||
// Tracks the first position where the *nested* offset is invalid
|
||||
private var firstInvalidatedPosition = sourceList.size
|
||||
|
||||
// This is where we handle changes to the *source* list.
|
||||
override fun sourceChanged(change: ListChangeListener.Change<out ObservableList<A>>) {
|
||||
beginChange()
|
||||
while (change.next()) {
|
||||
if (change.wasPermutated()) {
|
||||
// Update indexMap
|
||||
// If the source list was permuted we adjust 'nestedIndexOffsets' and translate the permutation to apply
|
||||
// to the nested elements.
|
||||
// For example:
|
||||
// original list: { {"a", "b"}, {"c", "d"}, {} }
|
||||
// original permutation: { 2, 1, 0 }
|
||||
// permuted list: { {}, {"c", "d"}, {"a", "b"} }
|
||||
// translated permutation: { 2, 3, 0, 1 }
|
||||
|
||||
// First we apply the permutation to the 'indexMap'
|
||||
val iterator = indexMap.iterator()
|
||||
for (entry in iterator) {
|
||||
val (wrapped, pair) = entry
|
||||
val (index, listener) = pair
|
||||
val (index, listener) = entry.value
|
||||
if (index >= change.from && index < change.to) {
|
||||
entry.setValue(Pair(change.getPermutation(index), listener))
|
||||
}
|
||||
}
|
||||
// Calculate the permuted sublist of nestedIndexOffsets
|
||||
// We apply the permutation to the relevant part of 'nestedIndexOffsets'.
|
||||
val newSubNestedIndexOffsets = IntArray(change.to - change.from)
|
||||
val firstTouched = if (change.from == 0) 0 else nestedIndexOffsets[change.from - 1]
|
||||
var currentOffset = firstTouched
|
||||
@ -95,6 +148,7 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
currentOffset += source[change.from + i].size
|
||||
newSubNestedIndexOffsets[i] = currentOffset
|
||||
}
|
||||
// Now we create the permutation array for the result list.
|
||||
val concatenatedPermutation = IntArray(newSubNestedIndexOffsets.last())
|
||||
// Set the non-permuted part
|
||||
var offset = 0
|
||||
@ -124,13 +178,14 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
throw UnsupportedOperationException("Updates not supported")
|
||||
} else {
|
||||
if (change.wasRemoved()) {
|
||||
// Update indexMap
|
||||
// If nested lists were removed we iterate over 'indexMap' and adjust the indices accordingly,
|
||||
// remove listeners and remove relevant mappings as well. We also invalidate nested offsets.
|
||||
val iterator = indexMap.iterator()
|
||||
for (entry in iterator) {
|
||||
val (wrapped, pair) = entry
|
||||
val (index, listener) = pair
|
||||
val removeEnd = change.from + change.removedSize
|
||||
if (index >= change.from) {
|
||||
val removeEnd = change.from + change.removedSize
|
||||
if (index < removeEnd) {
|
||||
wrapped.observableList.removeListener(listener)
|
||||
iterator.remove()
|
||||
@ -162,13 +217,9 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
indexMap[wrapped] = Pair(change.from + sublistIndex, createListener(wrapped))
|
||||
}
|
||||
invalidateOffsets(change.from)
|
||||
// We recalculate offsets early as we need the range anyway.
|
||||
recalculateOffsets()
|
||||
nextAdd(startingOffsetOf(change.from), nestedIndexOffsets[change.to - 1])
|
||||
for (i in change.from .. change.to - 1) {
|
||||
source[i].addListener { change: ListChangeListener.Change<out A> ->
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
recalculateOffsets()
|
||||
@ -176,18 +227,12 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
endChange()
|
||||
}
|
||||
|
||||
// Tracks the first position where the *nested* offset is invalid
|
||||
private var firstInvalidatedPosition = sourceList.size
|
||||
private fun invalidateOffsets(index: Int) {
|
||||
firstInvalidatedPosition = Math.min(firstInvalidatedPosition, index)
|
||||
}
|
||||
|
||||
private fun startingOffsetOf(listIndex: Int): Int {
|
||||
if (listIndex == 0) {
|
||||
return 0
|
||||
} else {
|
||||
return nestedIndexOffsets[listIndex - 1]
|
||||
}
|
||||
}
|
||||
|
||||
private fun recalculateOffsets() {
|
||||
if (firstInvalidatedPosition < source.size) {
|
||||
val firstInvalid = firstInvalidatedPosition
|
||||
|
@ -8,6 +8,7 @@ import javafx.collections.ObservableList
|
||||
import javafx.collections.transformation.TransformationList
|
||||
import org.eclipse.jetty.server.Authentication
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
/**
|
||||
* [FlattenedList] flattens the passed in list of [ObservableValue]s so that changes in individual updates to the values
|
||||
@ -106,7 +107,7 @@ class FlattenedList<A>(val sourceList: ObservableList<out ObservableValue<out A>
|
||||
}
|
||||
}
|
||||
endChange()
|
||||
require(sourceList.size == indexMap.size)
|
||||
assertEquals(sourceList.size, indexMap.size)
|
||||
}
|
||||
|
||||
override fun get(index: Int) = sourceList.get(index).value
|
||||
|
@ -17,6 +17,11 @@ class MapValuesList<K, A, C> private constructor(
|
||||
) : ObservableList<C> by exposedList {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* [create] is the factory of [MapValuesList].
|
||||
* @param sourceMap The source map.
|
||||
* @param assemble The function to be called for map each entry to construct the final list elements.
|
||||
*/
|
||||
fun <K, A, C> create(sourceMap: ObservableMap<K, A>, assemble: (Map.Entry<K, A>) -> C): MapValuesList<K, A, C> {
|
||||
val backingList = FXCollections.observableArrayList<Map.Entry<K, A>>(sourceMap.entries.sortedBy { it.key!!.hashCode() })
|
||||
return MapValuesList(sourceMap, backingList, backingList.map { assemble(it) })
|
||||
|
@ -9,6 +9,7 @@ import java.util.*
|
||||
/**
|
||||
* This is a variant of [EasyBind.map] where the mapped list is backed, therefore the mapping function will only be run
|
||||
* when an element is inserted or updated.
|
||||
* Use this instead of [EasyBind.map] to trade off memory vs CPU, or if (god forbid) the mapped function is side-effecting.
|
||||
*/
|
||||
class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : TransformationList<B, A>(list) {
|
||||
private val backingList = ArrayList<B>(list.size)
|
||||
@ -23,12 +24,14 @@ class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : Transf
|
||||
beginChange()
|
||||
while (change.next()) {
|
||||
if (change.wasPermutated()) {
|
||||
// Note how we don't re-run the mapping function on a permutation. If we supported mapIndexed we would
|
||||
// have to.
|
||||
val from = change.from
|
||||
val to = change.to
|
||||
val permutation = IntArray(to, { change.getPermutation(it) })
|
||||
val permutation = IntArray(to) { change.getPermutation(it) }
|
||||
val permutedSubList = ArrayList<B?>(to - from)
|
||||
permutedSubList.addAll(Collections.nCopies(to - from, null))
|
||||
for (i in 0 .. (to - from - 1)) {
|
||||
for (i in 0.until(to - from)) {
|
||||
permutedSubList[permutation[from + i]] = backingList[from + i]
|
||||
}
|
||||
permutedSubList.forEachIndexed { i, element ->
|
||||
@ -42,7 +45,7 @@ class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : Transf
|
||||
if (change.wasRemoved()) {
|
||||
val removePosition = change.from
|
||||
val removed = ArrayList<B>(change.removedSize)
|
||||
for (i in 0 .. change.removedSize - 1) {
|
||||
for (i in 0.until(change.removedSize)) {
|
||||
removed.add(backingList.removeAt(removePosition))
|
||||
}
|
||||
nextRemove(change.from, removed)
|
||||
@ -50,7 +53,7 @@ class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : Transf
|
||||
if (change.wasAdded()) {
|
||||
val addStart = change.from
|
||||
val addEnd = change.to
|
||||
for (i in addStart .. addEnd - 1) {
|
||||
for (i in addStart.until(addEnd)) {
|
||||
backingList.add(i, function(change.list[i]))
|
||||
}
|
||||
nextAdd(addStart, addEnd)
|
||||
|
@ -12,6 +12,12 @@ import rx.Observable
|
||||
* Simple utilities for converting an [rx.Observable] into a javafx [ObservableValue]/[ObservableList]
|
||||
*/
|
||||
|
||||
/**
|
||||
* [foldToObservableValue] takes an [rx.Observable] stream and creates an [ObservableValue] out of it.
|
||||
* @param initial The initial value of the returned observable.
|
||||
* @param folderFun The transformation function to be called on the observable value when a new element is emitted on
|
||||
* the stream.
|
||||
*/
|
||||
fun <A, B> Observable<A>.foldToObservableValue(initial: B, folderFun: (A, B) -> B): ObservableValue<B> {
|
||||
val result = SimpleObjectProperty<B>(initial)
|
||||
subscribe {
|
||||
@ -22,6 +28,13 @@ fun <A, B> Observable<A>.foldToObservableValue(initial: B, folderFun: (A, B) ->
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* [foldToObservableList] takes an [rx.Observable] stream and creates an [ObservableList] out of it, while maintaining
|
||||
* an accumulator.
|
||||
* @param initialAccumulator The initial value of the accumulator.
|
||||
* @param folderFun The transformation function to be called on the observable list when a new element is emitted on
|
||||
* the stream, which should modify the list as needed.
|
||||
*/
|
||||
fun <A, B, C> Observable<A>.foldToObservableList(
|
||||
initialAccumulator: C, folderFun: (A, C, ObservableList<B>) -> C
|
||||
): ObservableList<B> {
|
||||
@ -39,14 +52,21 @@ fun <A, B, C> Observable<A>.foldToObservableList(
|
||||
}
|
||||
|
||||
/**
|
||||
* This variant simply exposes all events in the list, in order of arrival.
|
||||
* [recordInSequence] records incoming events on the [rx.Observable] in sequence.
|
||||
*/
|
||||
fun <A> Observable<A>.foldToObservableList(): ObservableList<A> {
|
||||
fun <A> Observable<A>.recordInSequence(): ObservableList<A> {
|
||||
return foldToObservableList(Unit) { newElement, _unit, list ->
|
||||
list.add(newElement)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [foldToObservableMap] takes an [rx.Observable] stream and creates an [ObservableMap] out of it, while maintaining
|
||||
* an accumulator.
|
||||
* @param initialAccumulator The initial value of the accumulator.
|
||||
* @param folderFun The transformation function to be called on the observable map when a new element is emitted on
|
||||
* the stream, which should modify the map as needed.
|
||||
*/
|
||||
fun <A, B, K, C> Observable<A>.foldToObservableMap(
|
||||
initialAccumulator: C, folderFun: (A, C, ObservableMap<K, B>) -> C
|
||||
): ObservableMap<K, out B> {
|
||||
@ -68,7 +88,7 @@ fun <A, B, K, C> Observable<A>.foldToObservableMap(
|
||||
* @param toKey Function retrieving the key to associate with.
|
||||
* @param merge The function to be called if there is an existing element at the key.
|
||||
*/
|
||||
fun <A, K> Observable<A>.foldToObservableMap(
|
||||
fun <A, K> Observable<A>.recordAsAssociation(
|
||||
toKey: (A) -> K,
|
||||
merge: (K, oldValue: A, newValue: A) -> A = { _key, _oldValue, newValue -> newValue }
|
||||
): ObservableMap<K, out A> {
|
||||
|
@ -28,9 +28,17 @@ fun <A, B> ObservableValue<out A>.map(function: (A) -> B): ObservableValue<B> =
|
||||
/**
|
||||
* val dogs: ObservableList<Dog> = (..)
|
||||
* val dogOwners: ObservableList<Person> = dogs.map { it.owner }
|
||||
*
|
||||
* @param cached If true the results of the mapped function are cached in a backing list. If false each get() will
|
||||
* re-run the function.
|
||||
*/
|
||||
fun <A, B> ObservableList<out A>.map(function: (A) -> B): ObservableList<B> = MappedList(this, function)
|
||||
fun <A, B> ObservableList<out A>.mapNonBacked(function: (A) -> B): ObservableList<B> = EasyBind.map(this, function)
|
||||
fun <A, B> ObservableList<out A>.map(cached: Boolean = true, function: (A) -> B): ObservableList<B> {
|
||||
if (cached) {
|
||||
return MappedList(this, function)
|
||||
} else {
|
||||
return EasyBind.map(this, function)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* val aliceHeight: ObservableValue<Long> = (..)
|
||||
@ -126,12 +134,14 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
|
||||
fun <A> ObservableList<out ObservableValue<out A>>.flatten(): ObservableList<A> = FlattenedList(this)
|
||||
|
||||
/**
|
||||
* data class Person(val height: ObservableValue<Long>)
|
||||
* val people: List<Person> = listOf(alice, bob)
|
||||
* val heights: ObservableList<Long> = people.map(Person::height).sequence()
|
||||
*/
|
||||
fun <A> List<ObservableValue<out A>>.sequence(): ObservableList<A> = FlattenedList(FXCollections.observableArrayList(this))
|
||||
fun <A> Collection<ObservableValue<out A>>.sequence(): ObservableList<A> = FlattenedList(FXCollections.observableArrayList(this))
|
||||
|
||||
/**
|
||||
* data class Person(val height: Long)
|
||||
* val people: ObservableList<Person> = (..)
|
||||
* val nameToHeight: ObservableMap<String, Long> = people.associateBy(Person::name) { name, person -> person.height }
|
||||
*/
|
||||
@ -209,6 +219,7 @@ fun <A> ObservableList<ObservableList<A>>.concatenate(): ObservableList<A> {
|
||||
}
|
||||
|
||||
/**
|
||||
* data class Person(val name: String, val managerName: String)
|
||||
* val people: ObservableList<Person> = (..)
|
||||
* val managerEmployeeMapping: ObservableList<Pair<Person, ObservableList<Person>>> =
|
||||
* people.leftOuterJoin(people, Person::name, Person::managerName) { manager, employees -> Pair(manager, employees) }
|
||||
@ -225,6 +236,18 @@ fun <A : Any, B : Any, C, K : Any> ObservableList<A>.leftOuterJoin(
|
||||
}.concatenate()
|
||||
}
|
||||
|
||||
/**
|
||||
* data class Person(name: String, favouriteSpecies: Species)
|
||||
* data class Animal(name: String, species: Species)
|
||||
* val people: ObservableList<Person> = (..)
|
||||
* val animals: ObservableList<Animal> = (..)
|
||||
* val peopleToFavouriteAnimals: ObservableMap<Species, Pair<ObservableList<Person>, ObservableList<Animal>>> =
|
||||
* people.leftOuterJoin(animals, Person::favouriteSpecies, Animal::species)
|
||||
*
|
||||
* This is the most general left join, given a joining key it returns for each key a pair of relevant elements from the
|
||||
* left and right tables. It is "left outer" in the sense that all members of the left table are guaranteed to be in
|
||||
* the result, but this may not be the case for the right table.
|
||||
*/
|
||||
fun <A : Any, B : Any, K : Any> ObservableList<A>.leftOuterJoin(
|
||||
rightTable: ObservableList<B>,
|
||||
leftToJoinKey: (A) -> K,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.r3corda.client.model
|
||||
|
||||
import com.r3corda.client.fxutils.foldToObservableList
|
||||
import com.r3corda.client.fxutils.recordInSequence
|
||||
import com.r3corda.contracts.asset.Cash
|
||||
import com.r3corda.core.contracts.ContractState
|
||||
import com.r3corda.core.contracts.StateAndRef
|
||||
|
@ -5,6 +5,7 @@ import com.r3corda.client.fxutils.getObservableValue
|
||||
import com.r3corda.core.contracts.ContractState
|
||||
import com.r3corda.core.contracts.StateAndRef
|
||||
import com.r3corda.core.contracts.StateRef
|
||||
import com.r3corda.client.fxutils.recordInSequence
|
||||
import com.r3corda.core.crypto.SecureHash
|
||||
import com.r3corda.core.protocols.StateMachineRunId
|
||||
import com.r3corda.core.transactions.SignedTransaction
|
||||
|
@ -1,15 +1,17 @@
|
||||
package com.r3corda.client.fxutils
|
||||
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.fail
|
||||
|
||||
class AggregatedListTest {
|
||||
|
||||
var sourceList = FXCollections.observableArrayList<Int>()
|
||||
var aggregatedList = AggregatedList(sourceList, { it % 3 }) { mod3, group -> Pair(mod3, group) }
|
||||
var replayedList = ReplayedList(aggregatedList)
|
||||
lateinit var sourceList: ObservableList<Int>
|
||||
lateinit var aggregatedList: ObservableList<Pair<Int, ObservableList<Int>>>
|
||||
lateinit var replayedList: ObservableList<Pair<Int, ObservableList<Int>>>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -20,22 +22,22 @@ class AggregatedListTest {
|
||||
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedList.size == 0) { "Aggregation is empty is source list is" }
|
||||
assertEquals(replayedList.size, 0)
|
||||
|
||||
sourceList.add(9)
|
||||
require(replayedList.size == 1) { "Aggregation list has one element if one was added to source list" }
|
||||
require(replayedList[0]!!.first == 0)
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0]!!.first, 0)
|
||||
|
||||
sourceList.add(8)
|
||||
require(replayedList.size == 2) { "Aggregation list has two elements if two were added to source list with different keys" }
|
||||
assertEquals(replayedList.size, 2)
|
||||
|
||||
sourceList.add(6)
|
||||
require(replayedList.size == 2) { "Aggregation list's size doesn't change if element with existing key is added" }
|
||||
assertEquals(replayedList.size, 2)
|
||||
|
||||
replayedList.forEach {
|
||||
when (it.first) {
|
||||
0 -> require(it.second.toSet() == setOf(6, 9))
|
||||
2 -> require(it.second.size == 1)
|
||||
0 -> assertEquals(it.second.toSet(), setOf(6, 9))
|
||||
2 -> assertEquals(it.second.size, 1)
|
||||
else -> fail("No aggregation expected with key ${it.first}")
|
||||
}
|
||||
}
|
||||
@ -45,51 +47,51 @@ class AggregatedListTest {
|
||||
fun removeWorks() {
|
||||
sourceList.addAll(0, 1, 2, 3, 4)
|
||||
|
||||
require(replayedList.size == 3)
|
||||
assertEquals(replayedList.size, 3)
|
||||
replayedList.forEach {
|
||||
when (it.first) {
|
||||
0 -> require(it.second.toSet() == setOf(0, 3))
|
||||
1 -> require(it.second.toSet() == setOf(1, 4))
|
||||
2 -> require(it.second.toSet() == setOf(2))
|
||||
0 -> assertEquals(it.second.toSet(), setOf(0, 3))
|
||||
1 -> assertEquals(it.second.toSet(), setOf(1, 4))
|
||||
2 -> assertEquals(it.second.toSet(), setOf(2))
|
||||
else -> fail("No aggregation expected with key ${it.first}")
|
||||
}
|
||||
}
|
||||
|
||||
sourceList.remove(4)
|
||||
require(replayedList.size == 3)
|
||||
assertEquals(replayedList.size, 3)
|
||||
replayedList.forEach {
|
||||
when (it.first) {
|
||||
0 -> require(it.second.toSet() == setOf(0, 3))
|
||||
1 -> require(it.second.toSet() == setOf(1))
|
||||
2 -> require(it.second.toSet() == setOf(2))
|
||||
0 -> assertEquals(it.second.toSet(), setOf(0, 3))
|
||||
1 -> assertEquals(it.second.toSet(), setOf(1))
|
||||
2 -> assertEquals(it.second.toSet(), setOf(2))
|
||||
else -> fail("No aggregation expected with key ${it.first}")
|
||||
}
|
||||
}
|
||||
|
||||
sourceList.remove(2, 4)
|
||||
require(replayedList.size == 2)
|
||||
assertEquals(replayedList.size, 2)
|
||||
replayedList.forEach {
|
||||
when (it.first) {
|
||||
0 -> require(it.second.toSet() == setOf(0))
|
||||
1 -> require(it.second.toSet() == setOf(1))
|
||||
0 -> assertEquals(it.second.toSet(), setOf(0))
|
||||
1 -> assertEquals(it.second.toSet(), setOf(1))
|
||||
else -> fail("No aggregation expected with key ${it.first}")
|
||||
}
|
||||
}
|
||||
|
||||
sourceList.removeAll(0, 1)
|
||||
require(replayedList.size == 0)
|
||||
assertEquals(replayedList.size, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multipleElementsWithSameHashWorks() {
|
||||
sourceList.addAll(0, 0)
|
||||
require(replayedList.size == 1)
|
||||
assertEquals(replayedList.size, 1)
|
||||
replayedList.forEach {
|
||||
when (it.first) {
|
||||
0 -> {
|
||||
require(it.second.size == 2)
|
||||
require(it.second[0] == 0)
|
||||
require(it.second[1] == 0)
|
||||
assertEquals(it.second.size, 2)
|
||||
assertEquals(it.second[0], 0)
|
||||
assertEquals(it.second[1], 0)
|
||||
}
|
||||
else -> fail("No aggregation expected with key ${it.first}")
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
package com.r3corda.client.fxutils
|
||||
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.collections.ObservableMap
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AssociatedListTest {
|
||||
|
||||
var sourceList = FXCollections.observableArrayList(0)
|
||||
var associatedList = AssociatedList(sourceList, { it % 3 }) { mod3, number -> number }
|
||||
var replayedMap = ReplayedMap(associatedList)
|
||||
lateinit var sourceList: ObservableList<Int>
|
||||
lateinit var associatedList: ObservableMap<Int, Int>
|
||||
lateinit var replayedMap: ObservableMap<Int, Int>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -19,55 +22,55 @@ class AssociatedListTest {
|
||||
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedMap.size == 1)
|
||||
require(replayedMap[0] == 0)
|
||||
assertEquals(replayedMap.size, 1)
|
||||
assertEquals(replayedMap[0], 0)
|
||||
|
||||
sourceList.add(2)
|
||||
require(replayedMap.size == 2)
|
||||
require(replayedMap[0] == 0)
|
||||
require(replayedMap[2] == 2)
|
||||
assertEquals(replayedMap.size, 2)
|
||||
assertEquals(replayedMap[0], 0)
|
||||
assertEquals(replayedMap[2], 2)
|
||||
|
||||
sourceList.add(0, 4)
|
||||
require(replayedMap.size == 3)
|
||||
require(replayedMap[0] == 0)
|
||||
require(replayedMap[2] == 2)
|
||||
require(replayedMap[1] == 4)
|
||||
assertEquals(replayedMap.size, 3)
|
||||
assertEquals(replayedMap[0], 0)
|
||||
assertEquals(replayedMap[2], 2)
|
||||
assertEquals(replayedMap[1], 4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeWorks() {
|
||||
sourceList.addAll(2, 4)
|
||||
require(replayedMap.size == 3)
|
||||
assertEquals(replayedMap.size, 3)
|
||||
|
||||
sourceList.removeAt(0)
|
||||
require(replayedMap.size == 2)
|
||||
require(replayedMap[2] == 2)
|
||||
require(replayedMap[1] == 4)
|
||||
assertEquals(replayedMap.size, 2)
|
||||
assertEquals(replayedMap[2], 2)
|
||||
assertEquals(replayedMap[1], 4)
|
||||
|
||||
sourceList.add(1, 12)
|
||||
require(replayedMap.size == 3)
|
||||
require(replayedMap[2] == 2)
|
||||
require(replayedMap[1] == 4)
|
||||
require(replayedMap[0] == 12)
|
||||
assertEquals(replayedMap.size, 3)
|
||||
assertEquals(replayedMap[2], 2)
|
||||
assertEquals(replayedMap[1], 4)
|
||||
assertEquals(replayedMap[0], 12)
|
||||
|
||||
sourceList.clear()
|
||||
require(replayedMap.size == 0)
|
||||
assertEquals(replayedMap.size, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateWorks() {
|
||||
sourceList.addAll(2, 4)
|
||||
require(replayedMap.size == 3)
|
||||
assertEquals(replayedMap.size, 3)
|
||||
|
||||
sourceList[1] = 5
|
||||
require(replayedMap.size == 3)
|
||||
require(replayedMap[0] == 0)
|
||||
require(replayedMap[2] == 5)
|
||||
require(replayedMap[1] == 4)
|
||||
assertEquals(replayedMap.size, 3)
|
||||
assertEquals(replayedMap[0], 0)
|
||||
assertEquals(replayedMap[2], 5)
|
||||
assertEquals(replayedMap[1], 4)
|
||||
|
||||
sourceList.removeAt(1)
|
||||
require(replayedMap.size == 2)
|
||||
require(replayedMap[0] == 0)
|
||||
require(replayedMap[1] == 4)
|
||||
assertEquals(replayedMap.size, 2)
|
||||
assertEquals(replayedMap[0], 0)
|
||||
assertEquals(replayedMap[1], 4)
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ import javafx.collections.ObservableList
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ConcatenatedListTest {
|
||||
|
||||
var sourceList = FXCollections.observableArrayList<ObservableList<String>>(FXCollections.observableArrayList("hello"))
|
||||
var concatenatedList = ConcatenatedList(sourceList)
|
||||
var replayedList = ReplayedList(concatenatedList)
|
||||
lateinit var sourceList: ObservableList<ObservableList<String>>
|
||||
lateinit var concatenatedList: ConcatenatedList<String>
|
||||
lateinit var replayedList: ObservableList<String>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -19,42 +20,70 @@ class ConcatenatedListTest {
|
||||
replayedList = ReplayedList(concatenatedList)
|
||||
}
|
||||
|
||||
// A helper function for tests that checks internal invariants.
|
||||
fun <A> ConcatenatedList<A>.checkInvariants() {
|
||||
assertEquals(nestedIndexOffsets.size, source.size)
|
||||
var currentOffset = 0
|
||||
for (i in 0 .. source.size - 1) {
|
||||
currentOffset += source[i].size
|
||||
assertEquals(nestedIndexOffsets[i], currentOffset)
|
||||
}
|
||||
|
||||
assertEquals(indexMap.size, source.size)
|
||||
for (entry in indexMap) {
|
||||
val (wrapped, pair) = entry
|
||||
val index = pair.first
|
||||
val foundListIndices = ArrayList<Int>()
|
||||
source.forEachIndexed { i, list ->
|
||||
if (wrapped.observableList == list) {
|
||||
foundListIndices.add(i)
|
||||
}
|
||||
}
|
||||
require(foundListIndices.any { it == index })
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0] == "hello")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
|
||||
sourceList.add(FXCollections.observableArrayList("a", "b"))
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "a")
|
||||
require(replayedList[2] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "a")
|
||||
assertEquals(replayedList[2], "b")
|
||||
|
||||
sourceList.add(1, FXCollections.observableArrayList("c"))
|
||||
require(replayedList.size == 4)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "c")
|
||||
require(replayedList[2] == "a")
|
||||
require(replayedList[3] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 4)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "c")
|
||||
assertEquals(replayedList[2], "a")
|
||||
assertEquals(replayedList[3], "b")
|
||||
|
||||
sourceList[0].addAll("d", "e")
|
||||
require(replayedList.size == 6)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "d")
|
||||
require(replayedList[2] == "e")
|
||||
require(replayedList[3] == "c")
|
||||
require(replayedList[4] == "a")
|
||||
require(replayedList[5] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 6)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "d")
|
||||
assertEquals(replayedList[2], "e")
|
||||
assertEquals(replayedList[3], "c")
|
||||
assertEquals(replayedList[4], "a")
|
||||
assertEquals(replayedList[5], "b")
|
||||
|
||||
sourceList[1].add(0, "f")
|
||||
require(replayedList.size == 7)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "d")
|
||||
require(replayedList[2] == "e")
|
||||
require(replayedList[3] == "f")
|
||||
require(replayedList[4] == "c")
|
||||
require(replayedList[5] == "a")
|
||||
require(replayedList[6] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 7)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "d")
|
||||
assertEquals(replayedList[2], "e")
|
||||
assertEquals(replayedList[3], "f")
|
||||
assertEquals(replayedList[4], "c")
|
||||
assertEquals(replayedList[5], "a")
|
||||
assertEquals(replayedList[6], "b")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -65,38 +94,47 @@ class ConcatenatedListTest {
|
||||
sourceList[1].add(0, "f")
|
||||
|
||||
sourceList.removeAt(1)
|
||||
require(replayedList.size == 5)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "d")
|
||||
require(replayedList[2] == "e")
|
||||
require(replayedList[3] == "a")
|
||||
require(replayedList[4] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 5)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "d")
|
||||
assertEquals(replayedList[2], "e")
|
||||
assertEquals(replayedList[3], "a")
|
||||
assertEquals(replayedList[4], "b")
|
||||
|
||||
sourceList[0].clear()
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == "a")
|
||||
require(replayedList[1] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], "a")
|
||||
assertEquals(replayedList[1], "b")
|
||||
|
||||
sourceList[1].removeAt(0)
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0], "b")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun permutationWorks() {
|
||||
sourceList.addAll(FXCollections.observableArrayList("a", "b"), FXCollections.observableArrayList("c"))
|
||||
require(replayedList.size == 4)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "a")
|
||||
require(replayedList[2] == "b")
|
||||
require(replayedList[3] == "c")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 4)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "a")
|
||||
assertEquals(replayedList[2], "b")
|
||||
assertEquals(replayedList[3], "c")
|
||||
|
||||
sourceList.sortWith(object : Comparator<ObservableList<String>> {
|
||||
override fun compare(p0: ObservableList<String>, p1: ObservableList<String>): Int {
|
||||
return p0.size - p1.size
|
||||
}
|
||||
})
|
||||
require(replayedList.size == 4)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "c")
|
||||
require(replayedList[2] == "a")
|
||||
require(replayedList[3] == "b")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 4)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "c")
|
||||
assertEquals(replayedList[2], "a")
|
||||
assertEquals(replayedList[3], "b")
|
||||
|
||||
sourceList.add(0, FXCollections.observableArrayList("d", "e", "f"))
|
||||
sourceList.sortWith(object : Comparator<ObservableList<String>> {
|
||||
@ -104,14 +142,15 @@ class ConcatenatedListTest {
|
||||
return p0.size - p1.size
|
||||
}
|
||||
})
|
||||
require(replayedList.size == 7)
|
||||
require(replayedList[0] == "hello")
|
||||
require(replayedList[1] == "c")
|
||||
require(replayedList[2] == "a")
|
||||
require(replayedList[3] == "b")
|
||||
require(replayedList[4] == "d")
|
||||
require(replayedList[5] == "e")
|
||||
require(replayedList[6] == "f")
|
||||
concatenatedList.checkInvariants()
|
||||
assertEquals(replayedList.size, 7)
|
||||
assertEquals(replayedList[0], "hello")
|
||||
assertEquals(replayedList[1], "c")
|
||||
assertEquals(replayedList[2], "a")
|
||||
assertEquals(replayedList[3], "b")
|
||||
assertEquals(replayedList[4], "d")
|
||||
assertEquals(replayedList[5], "e")
|
||||
assertEquals(replayedList[6], "f")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,14 +2,16 @@ package com.r3corda.client.fxutils
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class FlattenedListTest {
|
||||
|
||||
var sourceList = FXCollections.observableArrayList(SimpleObjectProperty(1234))
|
||||
var flattenedList = FlattenedList(sourceList)
|
||||
var replayedList = ReplayedList(flattenedList)
|
||||
lateinit var sourceList: ObservableList<SimpleObjectProperty<Int>>
|
||||
lateinit var flattenedList: ObservableList<Int>
|
||||
lateinit var replayedList: ObservableList<Int>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -20,73 +22,73 @@ class FlattenedListTest {
|
||||
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0] == 1234)
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
|
||||
sourceList.add(SimpleObjectProperty(12))
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 12)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 12)
|
||||
|
||||
sourceList.add(SimpleObjectProperty(34))
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 12)
|
||||
require(replayedList[2] == 34)
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 12)
|
||||
assertEquals(replayedList[2], 34)
|
||||
|
||||
sourceList.add(0, SimpleObjectProperty(56))
|
||||
require(replayedList.size == 4)
|
||||
require(replayedList[0] == 56)
|
||||
require(replayedList[1] == 1234)
|
||||
require(replayedList[2] == 12)
|
||||
require(replayedList[3] == 34)
|
||||
assertEquals(replayedList.size, 4)
|
||||
assertEquals(replayedList[0], 56)
|
||||
assertEquals(replayedList[1], 1234)
|
||||
assertEquals(replayedList[2], 12)
|
||||
assertEquals(replayedList[3], 34)
|
||||
|
||||
sourceList.addAll(2, listOf(SimpleObjectProperty(78), SimpleObjectProperty(910)))
|
||||
require(replayedList.size == 6)
|
||||
require(replayedList[0] == 56)
|
||||
require(replayedList[1] == 1234)
|
||||
require(replayedList[2] == 78)
|
||||
require(replayedList[3] == 910)
|
||||
require(replayedList[4] == 12)
|
||||
require(replayedList[5] == 34)
|
||||
assertEquals(replayedList.size, 6)
|
||||
assertEquals(replayedList[0], 56)
|
||||
assertEquals(replayedList[1], 1234)
|
||||
assertEquals(replayedList[2], 78)
|
||||
assertEquals(replayedList[3], 910)
|
||||
assertEquals(replayedList[4], 12)
|
||||
assertEquals(replayedList[5], 34)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeWorks() {
|
||||
val firstRemoved = sourceList.removeAt(0)
|
||||
require(firstRemoved.get() == 1234)
|
||||
require(replayedList.size == 0)
|
||||
assertEquals(firstRemoved.get(), 1234)
|
||||
assertEquals(replayedList.size, 0)
|
||||
firstRemoved.set(123)
|
||||
|
||||
sourceList.add(SimpleObjectProperty(12))
|
||||
sourceList.add(SimpleObjectProperty(34))
|
||||
sourceList.add(SimpleObjectProperty(56))
|
||||
require(replayedList.size == 3)
|
||||
assertEquals(replayedList.size, 3)
|
||||
val secondRemoved = sourceList.removeAt(1)
|
||||
require(secondRemoved.get() == 34)
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 12)
|
||||
require(replayedList[1] == 56)
|
||||
assertEquals(secondRemoved.get(), 34)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 12)
|
||||
assertEquals(replayedList[1], 56)
|
||||
secondRemoved.set(123)
|
||||
|
||||
sourceList.clear()
|
||||
require(replayedList.size == 0)
|
||||
assertEquals(replayedList.size, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatingObservableWorks() {
|
||||
require(replayedList[0] == 1234)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
sourceList[0].set(4321)
|
||||
require(replayedList[0] == 4321)
|
||||
assertEquals(replayedList[0], 4321)
|
||||
|
||||
sourceList.add(0, SimpleObjectProperty(12))
|
||||
sourceList[1].set(8765)
|
||||
require(replayedList[0] == 12)
|
||||
require(replayedList[1] == 8765)
|
||||
assertEquals(replayedList[0], 12)
|
||||
assertEquals(replayedList[1], 8765)
|
||||
|
||||
sourceList[0].set(34)
|
||||
require(replayedList[0] == 34)
|
||||
require(replayedList[1] == 8765)
|
||||
assertEquals(replayedList[0], 34)
|
||||
assertEquals(replayedList[1], 8765)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -94,20 +96,20 @@ class FlattenedListTest {
|
||||
val observable = SimpleObjectProperty(12)
|
||||
sourceList.add(observable)
|
||||
sourceList.add(observable)
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 12)
|
||||
require(replayedList[2] == 12)
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 12)
|
||||
assertEquals(replayedList[2], 12)
|
||||
|
||||
observable.set(34)
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 34)
|
||||
require(replayedList[2] == 34)
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 34)
|
||||
assertEquals(replayedList[2], 34)
|
||||
|
||||
sourceList.removeAt(1)
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 34)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 34)
|
||||
}
|
||||
}
|
||||
|
@ -5,71 +5,72 @@ import javafx.collections.ObservableList
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class LeftOuterJoinedMapTest {
|
||||
|
||||
data class Person(val name: String, val age: Int)
|
||||
data class Dog(val name: String, val owner: String)
|
||||
|
||||
var people = FXCollections.observableArrayList<Person>(Person("Alice", 12))
|
||||
var dogs = FXCollections.observableArrayList<Dog>(Dog("Scruffy", owner = "Bob"))
|
||||
var joinedList = people.leftOuterJoin(dogs, Person::name, Dog::owner)
|
||||
// We replay the nested observable as well
|
||||
var replayedList = ReplayedList(joinedList.map { Pair(it.first, ReplayedList(it.second)) })
|
||||
lateinit var people: ObservableList<Person>
|
||||
lateinit var dogs: ObservableList<Dog>
|
||||
lateinit var joinedList: ObservableList<Pair<Person, ObservableList<Dog>>>
|
||||
lateinit var replayedList: ObservableList<out Pair<Person, ObservableList<Dog>>>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
people = FXCollections.observableArrayList<Person>(Person("Alice", 12))
|
||||
dogs = FXCollections.observableArrayList<Dog>(Dog("Scruffy", owner = "Bob"))
|
||||
joinedList = people.leftOuterJoin(dogs, Person::name, Dog::owner)
|
||||
joinedList = people.leftOuterJoin(dogs, Person::name, Dog::owner) { person, dogs -> Pair(person, dogs) }
|
||||
// We replay the nested observable as well
|
||||
replayedList = ReplayedList(joinedList.map { Pair(it.first, ReplayedList(it.second)) })
|
||||
}
|
||||
|
||||
// TODO perhaps these are too brittle because they test indices that are not stable. Use Expect dsl?
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 0)
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 0)
|
||||
|
||||
dogs.add(Dog("Scooby", owner = "Alice"))
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 1)
|
||||
require(replayedList[0].second[0].name == "Scooby")
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 1)
|
||||
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||
|
||||
people.add(Person("Bob", 34))
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 1)
|
||||
require(replayedList[0].second[0].name == "Scooby")
|
||||
require(replayedList[1].first.name == "Bob")
|
||||
require(replayedList[1].second.size == 1)
|
||||
require(replayedList[1].second[0].name == "Scruffy")
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 1)
|
||||
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||
assertEquals(replayedList[1].first.name, "Bob")
|
||||
assertEquals(replayedList[1].second.size, 1)
|
||||
assertEquals(replayedList[1].second[0].name, "Scruffy")
|
||||
|
||||
dogs.add(Dog("Bella", owner = "Bob"))
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 1)
|
||||
require(replayedList[0].second[0].name == "Scooby")
|
||||
require(replayedList[1].first.name == "Bob")
|
||||
require(replayedList[1].second.size == 2)
|
||||
require(replayedList[1].second[0].name == "Bella")
|
||||
require(replayedList[1].second[1].name == "Scruffy")
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 1)
|
||||
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||
assertEquals(replayedList[1].first.name, "Bob")
|
||||
assertEquals(replayedList[1].second.size, 2)
|
||||
assertEquals(replayedList[1].second[0].name, "Bella")
|
||||
assertEquals(replayedList[1].second[1].name, "Scruffy")
|
||||
|
||||
// We have another Alice wat
|
||||
people.add(Person("Alice", 91))
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 1)
|
||||
require(replayedList[0].second[0].name == "Scooby")
|
||||
require(replayedList[1].first.name == "Alice")
|
||||
require(replayedList[1].second.size == 1)
|
||||
require(replayedList[1].second[0].name == "Scooby")
|
||||
require(replayedList[2].first.name == "Bob")
|
||||
require(replayedList[2].second.size == 2)
|
||||
require(replayedList[2].second[0].name == "Bella")
|
||||
require(replayedList[2].second[1].name == "Scruffy")
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 1)
|
||||
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||
assertEquals(replayedList[1].first.name, "Alice")
|
||||
assertEquals(replayedList[1].second.size, 1)
|
||||
assertEquals(replayedList[1].second[0].name, "Scooby")
|
||||
assertEquals(replayedList[2].first.name, "Bob")
|
||||
assertEquals(replayedList[2].second.size, 2)
|
||||
assertEquals(replayedList[2].second[0].name, "Bella")
|
||||
assertEquals(replayedList[2].second[1].name, "Scruffy")
|
||||
|
||||
}
|
||||
|
||||
@ -79,37 +80,37 @@ class LeftOuterJoinedMapTest {
|
||||
people.add(Person("Bob", 34))
|
||||
dogs.add(Dog("Bella", owner = "Bob"))
|
||||
|
||||
require(people.removeAt(0).name == "Alice")
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0].first.name == "Bob")
|
||||
require(replayedList[0].second.size == 2)
|
||||
require(replayedList[0].second[0].name == "Bella")
|
||||
require(replayedList[0].second[1].name == "Scruffy")
|
||||
assertEquals(people.removeAt(0).name, "Alice")
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0].first.name, "Bob")
|
||||
assertEquals(replayedList[0].second.size, 2)
|
||||
assertEquals(replayedList[0].second[0].name, "Bella")
|
||||
assertEquals(replayedList[0].second[1].name, "Scruffy")
|
||||
|
||||
require(dogs.removeAt(0).name == "Scruffy")
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0].first.name == "Bob")
|
||||
require(replayedList[0].second.size == 1)
|
||||
require(replayedList[0].second[0].name == "Bella")
|
||||
assertEquals(dogs.removeAt(0).name, "Scruffy")
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0].first.name, "Bob")
|
||||
assertEquals(replayedList[0].second.size, 1)
|
||||
assertEquals(replayedList[0].second[0].name, "Bella")
|
||||
|
||||
people.add(Person("Alice", 213))
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 1)
|
||||
require(replayedList[0].second[0].name == "Scooby")
|
||||
require(replayedList[1].first.name == "Bob")
|
||||
require(replayedList[1].second.size == 1)
|
||||
require(replayedList[1].second[0].name == "Bella")
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 1)
|
||||
assertEquals(replayedList[0].second[0].name, "Scooby")
|
||||
assertEquals(replayedList[1].first.name, "Bob")
|
||||
assertEquals(replayedList[1].second.size, 1)
|
||||
assertEquals(replayedList[1].second[0].name, "Bella")
|
||||
|
||||
dogs.clear()
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0].first.name == "Alice")
|
||||
require(replayedList[0].second.size == 0)
|
||||
require(replayedList[1].first.name == "Bob")
|
||||
require(replayedList[1].second.size == 0)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0].first.name, "Alice")
|
||||
assertEquals(replayedList[0].second.size, 0)
|
||||
assertEquals(replayedList[1].first.name, "Bob")
|
||||
assertEquals(replayedList[1].second.size, 0)
|
||||
|
||||
people.clear()
|
||||
require(replayedList.size == 0)
|
||||
assertEquals(replayedList.size, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
package com.r3corda.client.fxutils
|
||||
|
||||
import org.junit.Before
|
||||
|
||||
class MapValuesListTest {
|
||||
|
||||
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
package com.r3corda.client.fxutils
|
||||
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.ObservableList
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class MappedListTest {
|
||||
|
||||
var sourceList = FXCollections.observableArrayList("Alice")
|
||||
var mappedList = MappedList(sourceList) { it.length }
|
||||
var replayedList = ReplayedList(mappedList)
|
||||
lateinit var sourceList: ObservableList<String>
|
||||
lateinit var mappedList: ObservableList<Int>
|
||||
lateinit var replayedList: ObservableList<Int>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -19,19 +21,19 @@ class MappedListTest {
|
||||
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0] == 5)
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0], 5)
|
||||
|
||||
sourceList.add("Bob")
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 5)
|
||||
require(replayedList[1] == 3)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 5)
|
||||
assertEquals(replayedList[1], 3)
|
||||
|
||||
sourceList.add(0, "Charlie")
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == 7)
|
||||
require(replayedList[1] == 5)
|
||||
require(replayedList[2] == 3)
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], 7)
|
||||
assertEquals(replayedList[1], 5)
|
||||
assertEquals(replayedList[2], 3)
|
||||
|
||||
}
|
||||
|
||||
@ -39,12 +41,12 @@ class MappedListTest {
|
||||
fun removeWorks() {
|
||||
sourceList.add("Bob")
|
||||
sourceList.add(0, "Charlie")
|
||||
require(replayedList.size == 3)
|
||||
assertEquals(replayedList.size, 3)
|
||||
|
||||
sourceList.removeAt(1)
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 7)
|
||||
require(replayedList[1] == 3)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 7)
|
||||
assertEquals(replayedList[1], 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -54,13 +56,13 @@ class MappedListTest {
|
||||
|
||||
sourceList.sortBy { it.length }
|
||||
|
||||
require(sourceList[0] == "Bob")
|
||||
require(sourceList[1] == "Alice")
|
||||
require(sourceList[2] == "Charlie")
|
||||
assertEquals(sourceList[0], "Bob")
|
||||
assertEquals(sourceList[1], "Alice")
|
||||
assertEquals(sourceList[2], "Charlie")
|
||||
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == 3)
|
||||
require(replayedList[1] == 5)
|
||||
require(replayedList[2] == 7)
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], 3)
|
||||
assertEquals(replayedList[1], 5)
|
||||
assertEquals(replayedList[2], 7)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.r3corda.client.fxutils
|
||||
import javafx.collections.FXCollections
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ReplayedListTest {
|
||||
|
||||
@ -17,70 +18,70 @@ class ReplayedListTest {
|
||||
|
||||
@Test
|
||||
fun addWorks() {
|
||||
require(replayedList.size == 1)
|
||||
require(replayedList[0] == 1234)
|
||||
assertEquals(replayedList.size, 1)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
|
||||
sourceList.add(12)
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 12)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 12)
|
||||
|
||||
sourceList.add(34)
|
||||
require(replayedList.size == 3)
|
||||
require(replayedList[0] == 1234)
|
||||
require(replayedList[1] == 12)
|
||||
require(replayedList[2] == 34)
|
||||
assertEquals(replayedList.size, 3)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
assertEquals(replayedList[1], 12)
|
||||
assertEquals(replayedList[2], 34)
|
||||
|
||||
sourceList.add(0, 56)
|
||||
require(replayedList.size == 4)
|
||||
require(replayedList[0] == 56)
|
||||
require(replayedList[1] == 1234)
|
||||
require(replayedList[2] == 12)
|
||||
require(replayedList[3] == 34)
|
||||
assertEquals(replayedList.size, 4)
|
||||
assertEquals(replayedList[0], 56)
|
||||
assertEquals(replayedList[1], 1234)
|
||||
assertEquals(replayedList[2], 12)
|
||||
assertEquals(replayedList[3], 34)
|
||||
|
||||
sourceList.addAll(2, listOf(78, 910))
|
||||
require(replayedList.size == 6)
|
||||
require(replayedList[0] == 56)
|
||||
require(replayedList[1] == 1234)
|
||||
require(replayedList[2] == 78)
|
||||
require(replayedList[3] == 910)
|
||||
require(replayedList[4] == 12)
|
||||
require(replayedList[5] == 34)
|
||||
assertEquals(replayedList.size, 6)
|
||||
assertEquals(replayedList[0], 56)
|
||||
assertEquals(replayedList[1], 1234)
|
||||
assertEquals(replayedList[2], 78)
|
||||
assertEquals(replayedList[3], 910)
|
||||
assertEquals(replayedList[4], 12)
|
||||
assertEquals(replayedList[5], 34)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeWorks() {
|
||||
val firstRemoved = sourceList.removeAt(0)
|
||||
require(firstRemoved == 1234)
|
||||
require(replayedList.size == 0)
|
||||
assertEquals(firstRemoved, 1234)
|
||||
assertEquals(replayedList.size, 0)
|
||||
|
||||
sourceList.add(12)
|
||||
sourceList.add(34)
|
||||
sourceList.add(56)
|
||||
require(replayedList.size == 3)
|
||||
assertEquals(replayedList.size, 3)
|
||||
val secondRemoved = sourceList.removeAt(1)
|
||||
require(secondRemoved == 34)
|
||||
require(replayedList.size == 2)
|
||||
require(replayedList[0] == 12)
|
||||
require(replayedList[1] == 56)
|
||||
assertEquals(secondRemoved, 34)
|
||||
assertEquals(replayedList.size, 2)
|
||||
assertEquals(replayedList[0], 12)
|
||||
assertEquals(replayedList[1], 56)
|
||||
|
||||
sourceList.clear()
|
||||
require(replayedList.size == 0)
|
||||
assertEquals(replayedList.size, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateWorks() {
|
||||
require(replayedList[0] == 1234)
|
||||
assertEquals(replayedList[0], 1234)
|
||||
sourceList[0] = 4321
|
||||
require(replayedList[0] == 4321)
|
||||
assertEquals(replayedList[0], 4321)
|
||||
|
||||
sourceList.add(0, 12)
|
||||
sourceList[1] = 8765
|
||||
require(replayedList[0] == 12)
|
||||
require(replayedList[1] == 8765)
|
||||
assertEquals(replayedList[0], 12)
|
||||
assertEquals(replayedList[1], 8765)
|
||||
|
||||
sourceList[0] = 34
|
||||
require(replayedList[0] == 34)
|
||||
require(replayedList[1] == 8765)
|
||||
assertEquals(replayedList[0], 34)
|
||||
assertEquals(replayedList[1], 8765)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,11 @@ package com.r3corda.client.fxutils
|
||||
|
||||
import javafx.collections.MapChangeListener
|
||||
import javafx.collections.ObservableMap
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
/**
|
||||
* [ReplayedMap] simply replays changes done to the source map. Used for testing changes.
|
||||
*/
|
||||
class ReplayedMap<K, A>(sourceMap: ObservableMap<K, A>) : ReadOnlyBackedObservableMapBase<K, A, Unit>() {
|
||||
init {
|
||||
sourceMap.forEach {
|
||||
@ -10,7 +14,7 @@ class ReplayedMap<K, A>(sourceMap: ObservableMap<K, A>) : ReadOnlyBackedObservab
|
||||
}
|
||||
sourceMap.addListener { change: MapChangeListener.Change<out K, out A> ->
|
||||
if (change.wasRemoved()) {
|
||||
require(backingMap.remove(change.key)!!.first == change.valueRemoved)
|
||||
assertEquals(backingMap.remove(change.key)!!.first, change.valueRemoved)
|
||||
}
|
||||
if (change.wasAdded()) {
|
||||
backingMap.set(change.key, Pair(change.valueAdded, Unit))
|
||||
|
Loading…
x
Reference in New Issue
Block a user