Skip to content
Snippets Groups Projects
Verified Commit 3c0e613b authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Additional new tests

parent 34cbb78a
Branches
No related tags found
No related merge requests found
Showing
with 462 additions and 169 deletions
......@@ -20,7 +20,6 @@
package de.kuschku.quasseldroid.protocol.io
import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
import de.kuschku.libquassel.protocol.io.print
import de.kuschku.quasseldroid.util.TlsInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
......@@ -67,12 +66,11 @@ class CoroutineChannel {
}
suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
buffer.print()
this.channel.write(buffer)
}
suspend fun write(chainedBuffer: ChainedByteBuffer) {
for (buffer in chainedBuffer.buffers()) {
for (buffer in chainedBuffer.iterator()) {
write(buffer)
}
}
......
......@@ -3,6 +3,7 @@ package de.kuschku.quasseldroid
import de.kuschku.libquassel.protocol.connection.ProtocolInfoSerializer
import de.kuschku.libquassel.protocol.features.FeatureSet
import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
import de.kuschku.libquassel.protocol.io.contentToString
import de.kuschku.libquassel.protocol.messages.handshake.ClientInit
import de.kuschku.libquassel.protocol.serializers.HandshakeSerializers
import de.kuschku.libquassel.protocol.serializers.handshake.ClientInitAckSerializer
......@@ -68,6 +69,7 @@ class ExampleUnitTest {
channel.write(sizeBuffer)
sizeBuffer.clear()
}
println(sendBuffer.toBuffer().contentToString())
channel.write(sendBuffer)
channel.flush()
sendBuffer.clear()
......
plugins {
kotlin("jvm")
}
dependencies {
implementation(kotlin("stdlib"))
}
......@@ -17,14 +17,10 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.protocol.io
package de.kuschku.codecoverage
import kotlin.concurrent.getOrSet
private val ascii = ThreadLocal<StringEncoder>()
private val utf8 = ThreadLocal<StringEncoder>()
private val utf16 = ThreadLocal<StringEncoder>()
fun stringEncoderAscii() = ascii.getOrSet { StringEncoder(Charsets.ISO_8859_1) }
fun stringEncoderUtf8() = utf8.getOrSet { StringEncoder(Charsets.UTF_8) }
fun stringEncoderUtf16() = utf16.getOrSet { StringEncoder(Charsets.UTF_16BE) }
/**
* Used to mark inline functions as generated for jacoco
*/
@Retention(AnnotationRetention.SOURCE)
annotation class Generated
......@@ -9,7 +9,7 @@ tasks.withType<Test> {
}
jacoco {
toolVersion = "0.8.6"
toolVersion = "0.8.3"
}
tasks.getByName<JacocoReport>("jacocoTestReport") {
......@@ -26,6 +26,7 @@ dependencies {
implementation(kotlin("stdlib"))
implementation("org.threeten", "threetenbp", "1.4.0")
api(project(":bitflags"))
api(project(":coverage-annotations"))
val junit5Version: String by project.extra
testImplementation("org.junit.jupiter", "junit-jupiter-api", junit5Version)
......
......@@ -21,49 +21,42 @@ package de.kuschku.libquassel.protocol.io
import java.nio.ByteBuffer
fun copyData(from: ByteBuffer, to: ByteBuffer, amount: Int = -1) {
val actualAmount =
if (amount >= 0) minOf(from.remaining(), to.remaining(), amount)
else minOf(from.remaining(), to.remaining())
for (i in 0 until actualAmount) {
to.put(from.get())
}/*
if (actualAmount > 0) {
val fromLimit = from.limit()
val toLimit = to.limit()
from.limit(from.position() + actualAmount)
to.limit(to.position() + actualAmount)
fun copyData(from: ByteBuffer, to: ByteBuffer, desiredAmount: Int = -1) {
val limit = from.limit()
val availableAmount = minOf(from.remaining(), to.remaining())
val amount =
if (desiredAmount < 0) availableAmount
else minOf(availableAmount, desiredAmount)
from.limit(from.position() + amount)
to.put(from)
from.limit(fromLimit)
to.limit(toLimit)
from.limit(limit)
}
*/
fun copyData(from: ByteBuffer, desiredAmount: Int): ByteBuffer {
val to = ByteBuffer.allocate(minOf(from.remaining(), desiredAmount))
copyData(from, to, desiredAmount)
return to.flip()
}
val alphabet = charArrayOf(
fun ByteBuffer?.isEmpty() = this == null || !this.hasRemaining()
private val alphabet = charArrayOf(
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
)
fun ByteBuffer.contentToString(): String {
mark()
val position = position()
val limit = limit()
var result = ""
while (remaining() > 0) {
while (hasRemaining()) {
val byte = get()
val upperNibble = byte.toInt() shr 4
val lowerNibble = byte.toInt() % 16
result += alphabet[(upperNibble + 16) % 16]
result += alphabet[(lowerNibble + 16) % 16]
}
reset()
limit(limit)
position(position)
return result
}
fun ByteBuffer.print() = println(contentToString())
fun copyData(from: ByteBuffer, amount: Int) = ByteBuffer.allocateDirect(amount).also {
if (amount > 0) {
copyData(from, it, amount)
it.clear()
}
}
......@@ -22,40 +22,31 @@ package de.kuschku.libquassel.protocol.io
import java.nio.ByteBuffer
import java.util.*
class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct: Boolean = false) {
class ChainedByteBuffer(
private val chunkSize: Int = 1024,
private val direct: Boolean = false,
private val limit: Long = 0,
) : Iterable<ByteBuffer> {
private val bufferList: MutableList<ByteBuffer> = ArrayList()
private var currentIndex = 0
var size = 0
private set
private var currentBuffer = 0
private fun allocate(size: Int) = when (direct) {
true -> ByteBuffer.allocateDirect(size)
false -> ByteBuffer.allocate(size)
}
private fun ensureSpace(size: Int) {
if (bufferList.isEmpty()) {
bufferList.add(allocate(bufferSize))
}
if (bufferList[currentBuffer].remaining() < size) {
currentBuffer += 1
private fun allocate(amount: Int): ByteBuffer {
require(limit <= 0 || size + amount <= limit) {
"Can not allocate $amount bytes, currently at $size, limit is $limit"
}
if (currentBuffer == bufferList.size) {
bufferList.add(allocate(bufferSize))
}
this.size += size
return if (direct) ByteBuffer.allocateDirect(amount)
else ByteBuffer.allocate(amount)
}
fun <T> withBuffer(length: Int = 0, f: (ByteBuffer) -> T): T {
ensureSpace(length)
val buffer = bufferList.last()
val positionBefore = buffer.position()
val result = f(buffer)
val positionAfter = buffer.position()
size += (positionAfter - positionBefore)
return result
private fun ensureSpace(requested: Int) {
if (bufferList.lastOrNull()?.remaining() ?: 0 < requested) {
bufferList.add(allocate(chunkSize))
}
size += requested
}
fun put(value: Byte) {
......@@ -64,12 +55,6 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct:
bufferList.last().put(value)
}
fun putChar(value: Char) {
ensureSpace(2)
bufferList.last().putChar(value)
}
fun putShort(value: Short) {
ensureSpace(2)
......@@ -101,9 +86,11 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct:
}
fun put(value: ByteBuffer) {
ensureSpace(value.remaining())
while (value.remaining() > 0) {
while (value.hasRemaining()) {
val requested = minOf(value.remaining(), chunkSize)
if (bufferList.lastOrNull()?.hasRemaining() != true) {
ensureSpace(requested)
}
copyData(value, bufferList.last())
}
}
......@@ -114,28 +101,29 @@ class ChainedByteBuffer(private val bufferSize: Int = 1024, private val direct:
fun clear() {
bufferList.clear()
currentBuffer = 0
size = 0
}
fun buffers() = sequence {
for (buffer in bufferList) {
buffer.flip()
val position = buffer.position()
val limit = buffer.limit()
yield(buffer)
buffer.position(position)
buffer.limit(limit)
}
}
override fun iterator() = ChainedByteBufferIterator(this)
fun toBuffer(): ByteBuffer {
val byteBuffer = allocate(bufferSize * bufferList.size)
for (buffer in bufferList) {
buffer.flip()
val byteBuffer = allocate(chunkSize * bufferList.size)
for (buffer in iterator()) {
byteBuffer.put(buffer)
}
byteBuffer.flip()
return byteBuffer
}
class ChainedByteBufferIterator(
private val buffer: ChainedByteBuffer
) : Iterator<ByteBuffer> {
private var index = 0
override fun hasNext() =
index < buffer.bufferList.size
override fun next(): ByteBuffer =
buffer.bufferList[index++].duplicate().flip()
}
}
......@@ -22,83 +22,71 @@ package de.kuschku.libquassel.protocol.io
import java.nio.ByteBuffer
import java.nio.CharBuffer
import java.nio.charset.Charset
import java.nio.charset.CoderResult
class StringEncoder(charset: Charset) {
private val encoder = charset.newEncoder()
private val decoder = charset.newDecoder()
private val charBuffer = CharBuffer.allocate(1024)
private fun charBuffer(length: Int): CharBuffer =
if (length < 1024) charBuffer.clear()
else CharBuffer.allocate(length)
fun encode(data: String?, target: ChainedByteBuffer) {
if (data == null) return
private fun charBuffer(length: Int): CharBuffer {
if (length < 1024) {
return charBuffer.clear()
} else {
return CharBuffer.allocate(length)
}
}
val charBuffer = charBuffer(data.length)
charBuffer.put(data)
charBuffer.flip()
private fun encodeInternal(data: CharBuffer): ByteBuffer {
encoder.reset()
var result: CoderResult
do {
result = target.withBuffer(charBuffer.remaining()) {
encoder.encode(charBuffer, it, true)
}
} while (result == CoderResult.OVERFLOW)
return encoder.encode(data)
}
fun encode(data: String?): ByteBuffer {
if (data == null) {
if (data == null || !encoder.canEncode(data)) {
return ByteBuffer.allocateDirect(0)
}
val charBuffer = charBuffer(data.length)
charBuffer.put(data)
charBuffer.flip()
encoder.reset()
return encoder.encode(charBuffer)
return encodeInternal(charBuffer)
}
fun encodeChar(data: Char?, target: ChainedByteBuffer) {
if (data == null) {
target.putShort(0)
} else {
target.putChar(data)
fun encodeChar(data: Char?): ByteBuffer {
if (!encoder.canEncode(data ?: '\u0000')) {
return ByteBuffer.allocateDirect(2)
}
val charBuffer = charBuffer(2)
charBuffer.put(data ?: '\u0000')
charBuffer.flip()
return encodeInternal(charBuffer)
}
fun decode(source: ByteBuffer, length: Int): String {
private fun decodeInternal(source: ByteBuffer, length: Int): CharBuffer {
val charBuffer = charBuffer(length)
val oldlimit = source.limit()
source.limit(source.position() + length)
decoder.reset()
decoder.decode(source, charBuffer, true).also {
if (it.isError) {
charBuffer.put('\uFFFD')
source.position(source.position() + it.length())
}
}
source.limit(oldlimit)
charBuffer.flip()
return charBuffer.toString()
return charBuffer.flip()
}
fun decode(source: ByteBuffer): String {
println("Called to decode ${source.contentToString()}")
val charBuffer = charBuffer(source.remaining())
decoder.reset()
decoder.decode(source, charBuffer, true).also {
if (it.isError) {
println("Encountered error: $it")
source.position(source.position() + it.length())
}
fun decode(source: ByteBuffer, length: Int): String {
return decodeInternal(source, length).toString()
}
charBuffer.flip()
println("Result: $charBuffer")
return charBuffer.toString()
fun decode(source: ByteBuffer): String {
return decodeInternal(source, source.remaining()).toString()
}
fun decodeChar(source: ByteBuffer): Char {
return source.getChar()
return decodeInternal(source, 2).get()
}
}
......@@ -30,7 +30,7 @@ object ByteBufferSerializer : QtSerializer<ByteBuffer?> {
override val javaType: Class<out ByteBuffer?> = ByteBuffer::class.java
override fun serialize(buffer: ChainedByteBuffer, data: ByteBuffer?, featureSet: FeatureSet) {
IntSerializer.serialize(buffer, data?.remaining() ?: 0, featureSet)
IntSerializer.serialize(buffer, data?.remaining() ?: -1, featureSet)
if (data != null) {
buffer.put(data)
}
......
......@@ -34,7 +34,7 @@ object QCharSerializer : QtSerializer<Char> {
private fun encoder() = encoderLocal.getOrSet { StringEncoder(Charsets.UTF_16BE) }
override fun serialize(buffer: ChainedByteBuffer, data: Char, featureSet: FeatureSet) {
encoder().encodeChar(data, buffer)
buffer.put(encoder().encodeChar(data))
}
override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Char {
......
......@@ -37,7 +37,7 @@ object QVariantSerializer : QtSerializer<QVariant_> {
override fun serialize(buffer: ChainedByteBuffer, data: QVariant_, featureSet: FeatureSet) {
IntSerializer.serialize(buffer, data.serializer.qtType.id, featureSet)
BoolSerializer.serialize(buffer, false, featureSet)
if (data is QVariant.Custom && data.serializer.qtType == QtType.UserType) {
if (data is QVariant.Custom) {
StringSerializerAscii.serialize(buffer, data.serializer.quasselType.typeName, featureSet)
}
data.serialize(buffer, featureSet)
......
......@@ -19,6 +19,7 @@
package de.kuschku.libquassel.protocol.types
import de.kuschku.codecoverage.Generated
import java.io.Serializable
typealias SignedIdType = Int
......@@ -35,8 +36,10 @@ interface SignedId<T> : Serializable, Comparable<SignedId<T>>
@Suppress("NOTHING_TO_INLINE")
@JvmName("isValidId")
@Generated
inline fun SignedId<SignedIdType>.isValid() = id > 0
@Suppress("NOTHING_TO_INLINE")
@JvmName("isValidId64")
@Generated
inline fun SignedId<SignedId64Type>.isValid() = id > 0
......@@ -35,7 +35,7 @@ typealias QVariantMap = Map<String, QVariant_>
typealias QStringList = List<String?>
sealed class QVariant<T> constructor(
internal val data: T,
private val data: T,
open val serializer: QtSerializer<T>,
) {
class Typed<T> internal constructor(data: T, serializer: QtSerializer<T>) :
......@@ -54,8 +54,6 @@ sealed class QVariant<T> constructor(
override fun toString() = when (data) {
is ByteBuffer ->
"QVariant(${serializer::class.java.simpleName}, ${data.contentToString()})"
is Array<*> ->
"QVariant(${serializer::class.java.simpleName}, ${Arrays.toString(data)})"
else ->
"QVariant(${serializer::class.java.simpleName}, $data)"
}
......@@ -65,6 +63,25 @@ sealed class QVariant<T> constructor(
if (serializer.javaType == T::class.java && this.value() is T) this as QVariant<T>
else null
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as QVariant<*>
if (data != other.data) return false
if (serializer != other.serializer) return false
return true
}
override fun hashCode(): Int {
var result = data?.hashCode() ?: 0
result = 31 * result + serializer.hashCode()
return result
}
companion object {
fun <T> of(data: T, serializer: QtSerializer<T>) = Typed(data, serializer)
fun <T> of(data: T, serializer: QuasselSerializer<T>) = Custom(data, serializer)
......
......@@ -14,34 +14,32 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
* with this program. If not, see <http:
*/
package de.kuschku.libquassel.protocol.variant
import java.util.*
enum class QtType(val id: kotlin.Int) {
Void(0),//, VoidSerializer),
Bool(1),//, BoolSerializer),
Int(2),//, IntSerializer),
UInt(3),//, UIntSerializer),
Void(0),
Bool(1),
Int(2),
UInt(3),
LongLong(4),
ULongLong(5),
Double(6),
QChar(7),//, CharSerializer),
QVariantMap(8),//, VariantMapSerializer),
QVariantList(9),//, VariantListSerializer),
QChar(7),
QVariantMap(8),
QVariantList(9),
QString(10),//, StringSerializer.UTF16),
QStringList(11),//, StringListSerializer),
QByteArray(12),//, ByteArraySerializer),
QString(10),
QStringList(11),
QByteArray(12),
QBitArray(13),
QDate(14),
QTime(15),//, TimeSerializer),
QDateTime(16),//, DateTimeSerializer),
QTime(15),
QDateTime(16),
QUrl(17),
QLocale(18),
......@@ -91,26 +89,22 @@ enum class QtType(val id: kotlin.Int) {
QQuaternion(86),
VoidStar(128),
Long(129),//, LongSerializer),
Short(130),//, ShortSerializer),
Char(131),//, ByteSerializer),
ULong(132),//, ULongSerializer),
Long(129),
Short(130),
Char(131),
ULong(132),
UShort(133),//, UShortSerializer),
UChar(134),//, UByteSerializer),
UShort(133),
UChar(134),
Float(135),
QObjectStar(136),
QWidgetStar(137),
QVariant(138),//, VariantSerializer),
QVariant(138),
User(256),
UserType(127);
val serializableName =
if (name.startsWith("Q")) name
else name.toLowerCase(Locale.ENGLISH)
companion object {
private val values = values().associateBy(QtType::id)
fun of(id: kotlin.Int): QtType? = values[id]
......
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.protocol.features
import de.kuschku.bitflags.none
import de.kuschku.bitflags.of
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class FeatureSetTest {
@Test
fun testParse() {
assertEquals(
emptyList<QuasselFeatureName>(),
FeatureSet.parse(
LegacyFeature.none(),
emptyList()
).featureList()
)
assertEquals(
listOf(
QuasselFeature.SynchronizedMarkerLine.feature,
QuasselFeature.ExtendedFeatures.feature,
QuasselFeatureName("_unknownFeature")
),
FeatureSet.parse(
LegacyFeature.of(
LegacyFeature.SynchronizedMarkerLine
),
listOf(
QuasselFeature.ExtendedFeatures.feature,
QuasselFeatureName("_unknownFeature")
)
).featureList()
)
}
@Test
fun testBuild() {
assertEquals(
emptyList<QuasselFeatureName>(),
FeatureSet.build(emptySet()).featureList()
)
assertEquals(
listOf(
QuasselFeature.SynchronizedMarkerLine.feature,
QuasselFeature.ExtendedFeatures.feature
),
FeatureSet.build(setOf(
QuasselFeature.SynchronizedMarkerLine,
QuasselFeature.ExtendedFeatures
)).featureList()
)
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.protocol.io
import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher
import org.hamcrest.MatcherAssert.assertThat
import org.junit.jupiter.api.Assertions.assertDoesNotThrow
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import java.lang.AssertionError
import java.nio.ByteBuffer
class ChainedByteBufferTest {
@Test
fun testPutArray() {
validateArray(byteArrayOf())
validateArray(byteArrayOf(0x00))
validateArray(byteArrayOf(0x01))
validateArray(byteArrayOf(0xFF.toByte()))
validateArray(byteArrayOf(
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
0x00, 0x01, -0x01, 0x00, 0x00, 0x01, -0x01, 0x00,
))
validateArray(ByteArray(3000, Int::toByte))
}
@Test
fun testLimit() {
assertThrows<IllegalArgumentException>(
"Can not allocate 10 bytes, currently at 50, limit is 50"
) {
ChainedByteBuffer(chunkSize = 10, limit = 50)
.put(ByteArray(70, Int::toByte))
}
assertDoesNotThrow {
ChainedByteBuffer(chunkSize = 10, limit = 70)
.put(ByteArray(70, Int::toByte))
}
assertDoesNotThrow {
ChainedByteBuffer(chunkSize = 10, limit = 70)
.put(ByteArray(50, Int::toByte))
}
assertDoesNotThrow {
ChainedByteBuffer(chunkSize = 10, limit = -1)
.put(ByteArray(50, Int::toByte))
}
assertDoesNotThrow {
ChainedByteBuffer(chunkSize = 10)
.put(ByteArray(50, Int::toByte))
}
}
@Test
fun testClear() {
val chained = ChainedByteBuffer(limit = 16384)
val array = ByteArray(3000, Int::toByte)
chained.put(array)
assertEquals(array.size, chained.size)
assertEquals(array.size, chained.toBuffer().remaining())
assertThat(chained.toBuffer(), ByteBufferMatcher(ByteBuffer.wrap(array)))
chained.clear()
assertEquals(0, chained.size)
assertEquals(0, chained.toBuffer().remaining())
assertThat(chained.toBuffer(), ByteBufferMatcher(ByteBuffer.allocate(0)))
}
private fun validateArray(array: ByteArray) {
fun validateArrayInternal(array: ByteArray, direct: Boolean) {
val bufferSize = 1024
val chained = ChainedByteBuffer(chunkSize = bufferSize, direct = direct, limit = 16384)
chained.put(array)
assertEquals(array.size, chained.size)
assertEquals(array.size, chained.toBuffer().remaining())
assertThat(chained.toBuffer(), ByteBufferMatcher(ByteBuffer.wrap(array)))
if (array.size < bufferSize && array.isNotEmpty()) {
assertEquals(array.size, chained.firstOrNull()?.remaining())
assertThat(chained.firstOrNull(), ByteBufferMatcher(ByteBuffer.wrap(array)))
assertEquals(1, chained.take(2).count())
}
}
validateArrayInternal(array, direct = true)
validateArrayInternal(array, direct = false)
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.protocol.io
import de.kuschku.libquassel.protocol.testutil.byteBufferOf
import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher
import org.hamcrest.MatcherAssert.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.nio.ByteBuffer
class StringEncoderTest {
private val ascii = StringEncoder(Charsets.ISO_8859_1)
private val utf8 = StringEncoder(Charsets.UTF_8)
private val utf16 = StringEncoder(Charsets.UTF_16BE)
@Test
fun testNullString() {
assertThat(
ascii.encode(null),
ByteBufferMatcher(ByteBuffer.allocate(0))
)
assertThat(
utf8.encode(null),
ByteBufferMatcher(ByteBuffer.allocate(0))
)
assertThat(
utf16.encode(null),
ByteBufferMatcher(ByteBuffer.allocate(0))
)
}
@Test
fun testNullChar() {
assertEquals(
1,
ascii.encodeChar(null).remaining()
)
assertThat(
ascii.encodeChar(null),
ByteBufferMatcher(byteBufferOf(0))
)
assertEquals(
1,
utf8.encodeChar(null).remaining()
)
assertThat(
utf8.encodeChar(null),
ByteBufferMatcher(byteBufferOf(0))
)
assertEquals(
2,
utf16.encodeChar(null).remaining()
)
assertThat(
utf16.encodeChar(null),
ByteBufferMatcher(byteBufferOf(0, 0)),
)
}
@Test
fun testUnencodableChar() {
assertEquals(
2,
ascii.encodeChar('\uFFFF').remaining()
)
assertThat(
ascii.encodeChar('\uFFFF'),
ByteBufferMatcher(byteBufferOf(0, 0))
)
}
}
......@@ -22,6 +22,7 @@ import de.kuschku.libquassel.protocol.testutil.byteBufferOf
import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher
import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
import org.junit.jupiter.api.Test
import java.nio.ByteBuffer
class ByteBufferSerializerTest {
@Test
......@@ -39,4 +40,30 @@ class ByteBufferSerializerTest {
byteBufferOf(0, 0, 0, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9),
::ByteBufferMatcher
)
@Test
fun testEmpty() = qtSerializerTest(
ByteBufferSerializer,
ByteBuffer.allocate(0),
byteBufferOf(0, 0, 0, 0),
::ByteBufferMatcher
)
@Test
fun testNull() {
qtSerializerTest(
ByteBufferSerializer,
null,
byteBufferOf(0, 0, 0, 0),
::ByteBufferMatcher,
serializeFeatureSet = null
)
qtSerializerTest(
ByteBufferSerializer,
null,
byteBufferOf(-1, -1, -1, -1),
::ByteBufferMatcher
)
}
}
......@@ -90,7 +90,7 @@ class DateTimeSerializerTest {
DateTimeSerializer,
LocalDateTime
.of(2019, Month.JANUARY, 15, 20, 25),
byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0x07u),
byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0xFFu),
matcher = ::TemporalMatcher
)
......
......@@ -80,7 +80,8 @@ class MessageSerializerTest {
// Content
0xFFu, 0xFFu, 0xFFu, 0xFFu,
),
deserializeFeatureSet = FeatureSet.all()
deserializeFeatureSet = FeatureSet.all(),
serializeFeatureSet = null
)
@Test
......@@ -105,7 +106,8 @@ class MessageSerializerTest {
""
),
byteBufferOf(-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
deserializeFeatureSet = FeatureSet.none()
deserializeFeatureSet = FeatureSet.none(),
serializeFeatureSet = FeatureSet.none()
)
@Test
......@@ -130,7 +132,8 @@ class MessageSerializerTest {
"äẞ\u0000\uFFFF"
),
byteBufferOf(127, -1, -1, -1, 90, -33, -109, -106, 0, 7, -1, -1, -113, 127, -1, -1, -1, 127, -1, -1, -1, 0, 15, 127, -1, -1, -1, 0, 0, 0, 9, -61, -92, -31, -70, -98, 0, -17, -65, -65, 0, 0, 0, 9, -61, -92, -31, -70, -98, 0, -17, -65, -65, 0, 0, 0, 9, -61, -92, -31, -70, -98, 0, -17, -65, -65),
deserializeFeatureSet = FeatureSet.none()
deserializeFeatureSet = FeatureSet.none(),
serializeFeatureSet = FeatureSet.none()
)
@Test
......@@ -157,5 +160,6 @@ class MessageSerializerTest {
byteBufferOf(0x7Fu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x13u, 0x87u, 0xFFu, 0xFFu, 0xD8u, 0xF0u, 0x00u, 0x07u, 0xFFu, 0xFFu, 0x8Fu, 0x7Fu, 0xFFu, 0xFFu, 0xFFu, 0x7Fu, 0xFFu, 0xFFu, 0xFFu, 0x00u, 0x0Fu, 0x7Fu, 0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x09u, 0xC3u, 0xA4u, 0xE1u, 0xBAu, 0x9Eu, 0x00u, 0xEFu, 0xBFu, 0xBFu, 0x00u, 0x00u, 0x00u, 0x09u, 0xC3u, 0xA4u, 0xE1u, 0xBAu, 0x9Eu, 0x00u, 0xEFu, 0xBFu, 0xBFu, 0x00u, 0x00u, 0x00u, 0x09u, 0xC3u, 0xA4u, 0xE1u, 0xBAu, 0x9Eu, 0x00u, 0xEFu, 0xBFu, 0xBFu, 0x00u, 0x00u, 0x00u, 0x09u, 0xC3u, 0xA4u, 0xE1u, 0xBAu, 0x9Eu, 0x00u, 0xEFu, 0xBFu, 0xBFu, 0x00u, 0x00u, 0x00u, 0x09u, 0xC3u, 0xA4u, 0xE1u, 0xBAu, 0x9Eu, 0x00u, 0xEFu, 0xBFu, 0xBFu, 0x00u, 0x00u, 0x00u, 0x09u, 0xC3u, 0xA4u, 0xE1u, 0xBAu, 0x9Eu, 0x00u, 0xEFu, 0xBFu, 0xBFu),
featureSets = listOf(FeatureSet.all()),
deserializeFeatureSet = FeatureSet.all(),
serializeFeatureSet = FeatureSet.all()
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment