From 075c4c06d3949c9e4d4716e57ee36131a8eefe1c Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Sat, 6 Feb 2021 23:06:39 +0100
Subject: [PATCH] Further tests

---
 app/build.gradle.kts                          |   9 +-
 bitflags/build.gradle.kts                     |  22 ++-
 .../de/kuschku/bitflags/Flag.kt               |   0
 .../main/kotlin/de/kuschku/bitflags/Flags.kt  |  25 +++
 .../de/kuschku/bitflags/none.kt}              |   5 +-
 .../src/main/kotlin/de/kuschku/bitflags/of.kt |  28 +++
 .../de/kuschku/bitflags/toBits.kt             |   0
 .../de/kuschku/bitflags/toEnumSet.kt}         |  16 +-
 .../de/kuschku/bitflags/toFlag.kt             |  32 ++--
 .../kotlin/de/kuschku/bitflags/validValues.kt |  62 +++++++
 build.gradle.kts                              |   4 +-
 protocol/build.gradle.kts                     |  25 ++-
 .../protocol/features/FeatureSet.kt           |   2 +-
 .../protocol/features/LegacyFeature.kt        |   4 +-
 .../primitive/DateTimeSerializer.kt           |   8 +-
 .../serializers/primitive/DoubleSerializer.kt |  38 +++++
 .../serializers/primitive/FloatSerializer.kt  |  38 +++++
 .../primitive/MessageSerializer.kt            |   2 +-
 .../primitive/QVariantSerializer.kt           |   2 +-
 .../serializers/primitive/Serializers.kt      |  28 +--
 .../serializers/primitive/StringSerializer.kt |   8 +-
 .../primitive/StringSerializerUtf16.kt        |   2 +-
 .../primitive/StringSerializerUtf8.kt         |   2 +-
 .../serializers/primitive/TimeSerializer.kt   |   4 +-
 .../protocol/types/BufferActivity.kt          |   4 +-
 .../libquassel/protocol/types/BufferInfo.kt   |   4 +-
 .../libquassel/protocol/types/BufferType.kt   |   3 +-
 .../libquassel/protocol/types/MessageFlag.kt  |   3 +-
 .../libquassel/protocol/types/MessageType.kt  |   3 +-
 .../libquassel/protocol/variant/QVariant.kt   |  36 +---
 .../libquassel/protocol/variant/QtType.kt     |   3 +-
 .../protocol/variant/QuasselType.kt           |   3 +-
 .../handshake/ClientInitSerializerTest.kt     |  43 ++---
 .../primitive/BoolSerializerTest.kt           |  31 ++--
 .../primitive/BufferInfoSerializerTest.kt     |  57 +++++++
 .../primitive/ByteBufferSerializerTest.kt     |  32 ++--
 .../primitive/ByteSerializerTest.kt           |  60 +++----
 .../primitive/DateTimeSerializerTest.kt       | 107 ++++++++++++
 .../primitive/DoubleSerializerTest.kt         |  67 ++++++++
 .../primitive/FloatSerializerTest.kt          |  67 ++++++++
 .../primitive/IntSerializerTest.kt            |  60 +++----
 .../primitive/LongSerializerTest.kt           |  60 +++----
 .../primitive/MessageSerializerTest.kt        | 161 ++++++++++++++++++
 .../primitive/QCharSerializerTest.kt          | 110 +++++-------
 .../primitive/QVariantSerializerTest.kt       |  86 ++++++++++
 .../primitive/ShortSerializerTest.kt          |  60 +++----
 .../primitive/StringSerializerTest.kt         |  57 ++-----
 .../primitive/UByteSerializerTest.kt          |  54 ++++++
 .../primitive/UIntSerializerTest.kt           |  54 ++++++
 .../primitive/ULongSerializerTest.kt          |  53 ++++++
 .../primitive/UShortSerializerTest.kt         |  54 ++++++
 .../protocol/testutil/byteBufferOf.kt         |   3 -
 .../protocol/testutil/deserialize.kt          |  69 +++++---
 .../testutil/handshakeSerializerTest.kt       |  45 +++++
 .../testutil/matchers/BomMatcherChar.kt       |   1 -
 .../testutil/matchers/BomMatcherString.kt     |   9 +-
 .../testutil/matchers/ByteBufferMatcher.kt    |  32 +++-
 .../testutil/matchers/TemporalMatcher.kt      |  52 ++++++
 .../protocol/testutil/qtSerializerTest.kt     |  44 +++++
 .../testutil/quasselSerializerTest.kt         |  45 +++++
 .../testutil/testHandshakeSerializerDirect.kt |  21 +--
 .../testHandshakeSerializerEncoded.kt         |  29 ++--
 .../testutil/testQtSerializerDirect.kt        |  27 ++-
 .../testutil/testQtSerializerVariant.kt       |  27 +--
 .../testutil/testQuasselSerializerDirect.kt   |  46 +++++
 .../testutil/testQuasselSerializerVariant.kt  |  48 ++++++
 66 files changed, 1663 insertions(+), 533 deletions(-)
 rename bitflags/src/main/{java => kotlin}/de/kuschku/bitflags/Flag.kt (100%)
 create mode 100644 bitflags/src/main/kotlin/de/kuschku/bitflags/Flags.kt
 rename bitflags/src/main/{java/de/kuschku/bitflags/toEnumSet.kt => kotlin/de/kuschku/bitflags/none.kt} (79%)
 create mode 100644 bitflags/src/main/kotlin/de/kuschku/bitflags/of.kt
 rename bitflags/src/main/{java => kotlin}/de/kuschku/bitflags/toBits.kt (100%)
 rename bitflags/src/main/{java/de/kuschku/bitflags/Flags.kt => kotlin/de/kuschku/bitflags/toEnumSet.kt} (66%)
 rename bitflags/src/main/{java => kotlin}/de/kuschku/bitflags/toFlag.kt (66%)
 create mode 100644 bitflags/src/main/kotlin/de/kuschku/bitflags/validValues.kt
 create mode 100644 protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializer.kt
 create mode 100644 protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializer.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BufferInfoSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/handshakeSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/TemporalMatcher.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/qtSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/quasselSerializerTest.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerDirect.kt
 create mode 100644 protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerVariant.kt

diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e719474e3..9a7edc30d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
 plugins {
   id("com.android.application")
   id("kotlin-android")
@@ -23,6 +21,10 @@ android {
     val androidxComposeVersion: String by project.extra
     kotlinCompilerExtensionVersion = androidxComposeVersion
   }
+
+  kotlinOptions {
+    useIR = true
+  }
 }
 
 kapt {
@@ -65,7 +67,8 @@ dependencies {
   implementation("io.coil-kt", "coil", "1.1.1")
   implementation("dev.chrisbanes.accompanist", "accompanist-coil", "0.5.0")
 
-  testImplementation("junit", "junit", "4.13.1")
+  val junit4Version: String by project.extra
+  testImplementation("junit", "junit", junit4Version)
   androidTestImplementation("androidx.test.ext", "junit", "1.1.2")
   androidTestImplementation("androidx.test.espresso", "espresso-core", "3.3.0")
 }
diff --git a/bitflags/build.gradle.kts b/bitflags/build.gradle.kts
index 8c7ddc04e..393ac2018 100644
--- a/bitflags/build.gradle.kts
+++ b/bitflags/build.gradle.kts
@@ -1,9 +1,29 @@
 plugins {
   kotlin("jvm")
+  id("jacoco")
+}
+
+tasks.withType<Test> {
+  useJUnitPlatform()
+}
+
+tasks.getByName<JacocoReport>("jacocoTestReport") {
+  reports {
+    sourceDirectories.from(fileTree("src/main/kotlin"))
+    classDirectories.from(fileTree("build/classes"))
+    xml.destination = File("$buildDir/reports/jacoco/report.xml")
+    html.isEnabled = true
+    xml.isEnabled = true
+    csv.isEnabled = false
+  }
 }
 
 dependencies {
   implementation(kotlin("stdlib"))
 
-  testImplementation("junit", "junit", "4.13.1")
+  val junit5Version: String by project.extra
+  testImplementation("org.junit.jupiter", "junit-jupiter-api", junit5Version)
+  testRuntimeOnly("org.junit.jupiter", "junit-jupiter-engine", junit5Version)
+  val hamcrestVersion: String by project.extra
+  testImplementation("org.hamcrest", "hamcrest-library", hamcrestVersion)
 }
diff --git a/bitflags/src/main/java/de/kuschku/bitflags/Flag.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/Flag.kt
similarity index 100%
rename from bitflags/src/main/java/de/kuschku/bitflags/Flag.kt
rename to bitflags/src/main/kotlin/de/kuschku/bitflags/Flag.kt
diff --git a/bitflags/src/main/kotlin/de/kuschku/bitflags/Flags.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/Flags.kt
new file mode 100644
index 000000000..cc6a8daa3
--- /dev/null
+++ b/bitflags/src/main/kotlin/de/kuschku/bitflags/Flags.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.bitflags
+
+interface Flags<T, U> where U : Flag<T>, U : Enum<U> {
+  operator fun get(value: T): U?
+  val all: Set<U>
+}
diff --git a/bitflags/src/main/java/de/kuschku/bitflags/toEnumSet.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/none.kt
similarity index 79%
rename from bitflags/src/main/java/de/kuschku/bitflags/toEnumSet.kt
rename to bitflags/src/main/kotlin/de/kuschku/bitflags/none.kt
index eb000188f..da9953d99 100644
--- a/bitflags/src/main/java/de/kuschku/bitflags/toEnumSet.kt
+++ b/bitflags/src/main/kotlin/de/kuschku/bitflags/none.kt
@@ -21,6 +21,5 @@ package de.kuschku.bitflags
 
 import java.util.*
 
-inline fun <reified T : Enum<T>> List<T>.toEnumSet(): EnumSet<T> =
-  if (this.isEmpty()) EnumSet.noneOf(T::class.java)
-  else EnumSet.of(this.first(), *this.subList(1, this.size).toTypedArray())
+inline fun <reified T> Flags<*, T>.none(): EnumSet<T>
+  where T : Flag<*>, T : Enum<T> = EnumSet.noneOf(T::class.java)
diff --git a/bitflags/src/main/kotlin/de/kuschku/bitflags/of.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/of.kt
new file mode 100644
index 000000000..7398893ff
--- /dev/null
+++ b/bitflags/src/main/kotlin/de/kuschku/bitflags/of.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.bitflags
+
+import java.util.*
+
+inline fun <reified T> Flags<*, T>.of(vararg values: T): EnumSet<T>
+  where T : Flag<*>, T : Enum<T> = values.toEnumSet()
+
+inline fun <reified T> Flags<*, T>.of(values: Collection<T>): EnumSet<T>
+  where T : Flag<*>, T : Enum<T> = values.toEnumSet()
diff --git a/bitflags/src/main/java/de/kuschku/bitflags/toBits.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/toBits.kt
similarity index 100%
rename from bitflags/src/main/java/de/kuschku/bitflags/toBits.kt
rename to bitflags/src/main/kotlin/de/kuschku/bitflags/toBits.kt
diff --git a/bitflags/src/main/java/de/kuschku/bitflags/Flags.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/toEnumSet.kt
similarity index 66%
rename from bitflags/src/main/java/de/kuschku/bitflags/Flags.kt
rename to bitflags/src/main/kotlin/de/kuschku/bitflags/toEnumSet.kt
index e29c2b7cf..23ca9dcc2 100644
--- a/bitflags/src/main/java/de/kuschku/bitflags/Flags.kt
+++ b/bitflags/src/main/kotlin/de/kuschku/bitflags/toEnumSet.kt
@@ -21,24 +21,12 @@ package de.kuschku.bitflags
 
 import java.util.*
 
-interface Flags<T, U> where U: Flag<T>, U: Enum<U> {
-  operator fun get(value: T): U?
-  fun all(): Collection<U>
-}
-
-inline fun <reified T> Flags<*, T>.of(
-  vararg values: T
-) where T: Flag<*>, T: Enum<T> = values.toEnumSet()
-inline fun <reified T> Flags<*, T>.of(
-  values: Collection<T>
-) where T: Flag<*>, T: Enum<T> = values.toEnumSet()
-
-inline fun <reified T: Enum<T>> Array<out T>.toEnumSet() =
+inline fun <reified T : Enum<T>> Array<out T>.toEnumSet() =
   EnumSet.noneOf(T::class.java).apply {
     addAll(this@toEnumSet)
   }
 
-inline fun <reified T: Enum<T>> Collection<T>.toEnumSet() =
+inline fun <reified T : Enum<T>> Collection<T>.toEnumSet() =
   EnumSet.noneOf(T::class.java).apply {
     addAll(this@toEnumSet)
   }
diff --git a/bitflags/src/main/java/de/kuschku/bitflags/toFlag.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/toFlag.kt
similarity index 66%
rename from bitflags/src/main/java/de/kuschku/bitflags/toFlag.kt
rename to bitflags/src/main/kotlin/de/kuschku/bitflags/toFlag.kt
index 8018a4444..1eb55bb51 100644
--- a/bitflags/src/main/java/de/kuschku/bitflags/toFlag.kt
+++ b/bitflags/src/main/kotlin/de/kuschku/bitflags/toFlag.kt
@@ -22,42 +22,42 @@ package de.kuschku.bitflags
 import java.util.*
 import kotlin.experimental.and
 
-inline fun <reified T> Flags<Byte, T>.of(value: Byte?): EnumSet<T> where T: Flag<Byte>, T: Enum<T> {
+inline fun <reified T> Flags<Byte, T>.of(value: Byte?): EnumSet<T> where T : Flag<Byte>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0.toByte() }.toEnumSet()
+  return all.filter { (value and it.value) != 0.toByte() }.toEnumSet()
 }
 
-inline fun <reified T> Flags<UByte, T>.of(value: UByte?): EnumSet<T> where T: Flag<UByte>, T: Enum<T> {
+inline fun <reified T> Flags<UByte, T>.of(value: UByte?): EnumSet<T> where T : Flag<UByte>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0.toUByte() }.toEnumSet()
+  return all.filter { (value and it.value) != 0.toUByte() }.toEnumSet()
 }
 
-inline fun <reified T> Flags<Short, T>.of(value: Short?): EnumSet<T> where T: Flag<Short>, T: Enum<T> {
+inline fun <reified T> Flags<Short, T>.of(value: Short?): EnumSet<T> where T : Flag<Short>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0.toShort() }.toEnumSet()
+  return all.filter { (value and it.value) != 0.toShort() }.toEnumSet()
 }
 
-inline fun <reified T> Flags<UShort, T>.of(value: UShort?): EnumSet<T> where T: Flag<UShort>, T: Enum<T> {
+inline fun <reified T> Flags<UShort, T>.of(value: UShort?): EnumSet<T> where T : Flag<UShort>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0.toUShort() }.toEnumSet()
+  return all.filter { (value and it.value) != 0.toUShort() }.toEnumSet()
 }
 
-inline fun <reified T> Flags<Int, T>.of(value: Int?): EnumSet<T> where T: Flag<Int>, T: Enum<T> {
+inline fun <reified T> Flags<Int, T>.of(value: Int?): EnumSet<T> where T : Flag<Int>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0 }.toEnumSet()
+  return all.filter { (value and it.value) != 0 }.toEnumSet()
 }
 
-inline fun <reified T> Flags<UInt, T>.of(value: UInt?): EnumSet<T> where T: Flag<UInt>, T: Enum<T> {
+inline fun <reified T> Flags<UInt, T>.of(value: UInt?): EnumSet<T> where T : Flag<UInt>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0u }.toEnumSet()
+  return all.filter { (value and it.value) != 0u }.toEnumSet()
 }
 
-inline fun <reified T> Flags<Long, T>.of(value: Long?): EnumSet<T> where T: Flag<Long>, T: Enum<T> {
+inline fun <reified T> Flags<Long, T>.of(value: Long?): EnumSet<T> where T : Flag<Long>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0L }.toEnumSet()
+  return all.filter { (value and it.value) != 0L }.toEnumSet()
 }
 
-inline fun <reified T> Flags<ULong, T>.of(value: ULong?): EnumSet<T> where T: Flag<ULong>, T: Enum<T> {
+inline fun <reified T> Flags<ULong, T>.of(value: ULong?): EnumSet<T> where T : Flag<ULong>, T : Enum<T> {
   if (value == null) return emptyList<T>().toEnumSet()
-  return this.all().filter { (value and it.value) != 0uL }.toEnumSet()
+  return all.filter { (value and it.value) != 0uL }.toEnumSet()
 }
diff --git a/bitflags/src/main/kotlin/de/kuschku/bitflags/validValues.kt b/bitflags/src/main/kotlin/de/kuschku/bitflags/validValues.kt
new file mode 100644
index 000000000..3485bf8a2
--- /dev/null
+++ b/bitflags/src/main/kotlin/de/kuschku/bitflags/validValues.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.bitflags
+
+import java.util.*
+
+@JvmName("validValuesUByte")
+inline fun <reified T> Flags<UByte, T>.validValues(): EnumSet<T>
+  where T : Flag<UByte>, T : Enum<T> =
+  all.filter { it.value != 0.toUByte() }.toEnumSet()
+
+@JvmName("validValuesByte")
+inline fun <reified T> Flags<Byte, T>.validValues(): EnumSet<T>
+  where T : Flag<Byte>, T : Enum<T> =
+  all.filter { it.value != 0.toByte() }.toEnumSet()
+
+@JvmName("validValuesUShort")
+inline fun <reified T> Flags<UShort, T>.validValues(): EnumSet<T>
+  where T : Flag<UShort>, T : Enum<T> =
+  all.filter { it.value != 0.toUShort() }.toEnumSet()
+
+@JvmName("validValuesShort")
+inline fun <reified T> Flags<Short, T>.validValues(): EnumSet<T>
+  where T : Flag<Short>, T : Enum<T> =
+  all.filter { it.value != 0.toShort() }.toEnumSet()
+
+@JvmName("validValuesUInt")
+inline fun <reified T> Flags<UInt, T>.validValues(): EnumSet<T>
+  where T : Flag<UInt>, T : Enum<T> =
+  all.filter { it.value != 0u }.toEnumSet()
+
+@JvmName("validValuesInt")
+inline fun <reified T> Flags<Int, T>.validValues(): EnumSet<T>
+  where T : Flag<Int>, T : Enum<T> =
+  all.filter { it.value != 0 }.toEnumSet()
+
+@JvmName("validValuesULong")
+inline fun <reified T> Flags<ULong, T>.validValues(): EnumSet<T>
+  where T : Flag<ULong>, T : Enum<T> =
+  all.filter { it.value != 0uL }.toEnumSet()
+
+@JvmName("validValuesLong")
+inline fun <reified T> Flags<Long, T>.validValues(): EnumSet<T>
+  where T : Flag<Long>, T : Enum<T> =
+  all.filter { it.value != 0L }.toEnumSet()
diff --git a/build.gradle.kts b/build.gradle.kts
index 5368ece0a..c3ad86bf6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -41,6 +41,9 @@ allprojects {
   extra["androidxLifecycleVersion"] = "2.3.0-rc01"
   extra["androidxMultidexVersion"] = "2.0.1"
   extra["daggerHiltVersion"] = "2.31.2-alpha"
+  extra["hamcrestVersion"] = "2.1"
+  extra["junit4Version"] = "4.13.1"
+  extra["junit5Version"] = "5.3.1"
   extra["mdcVersion"] = "1.2.1"
 
   repositories {
@@ -56,7 +59,6 @@ allprojects {
         "-Xopt-in=kotlin.ExperimentalUnsignedTypes"
       )
       jvmTarget = "1.8"
-      useIR = true
     }
   }
 }
diff --git a/protocol/build.gradle.kts b/protocol/build.gradle.kts
index 2bfaa2a2f..27124f8ee 100644
--- a/protocol/build.gradle.kts
+++ b/protocol/build.gradle.kts
@@ -1,5 +1,24 @@
 plugins {
   kotlin("jvm")
+  jacoco
+}
+
+tasks.withType<Test> {
+  useJUnitPlatform()
+}
+
+jacoco {
+  toolVersion = "0.8.6"
+}
+
+tasks.getByName<JacocoReport>("jacocoTestReport") {
+  reports {
+    sourceDirectories.from(fileTree("src/main/kotlin"))
+    xml.destination = File("$buildDir/reports/jacoco/report.xml")
+    html.isEnabled = true
+    xml.isEnabled = true
+    csv.isEnabled = false
+  }
 }
 
 dependencies {
@@ -7,5 +26,9 @@ dependencies {
   implementation("org.threeten", "threetenbp", "1.4.0")
   api(project(":bitflags"))
 
-  testImplementation("junit", "junit", "4.13.1")
+  val junit5Version: String by project.extra
+  testImplementation("org.junit.jupiter", "junit-jupiter-api", junit5Version)
+  testRuntimeOnly("org.junit.jupiter", "junit-jupiter-engine", junit5Version)
+  val hamcrestVersion: String by project.extra
+  testImplementation("org.hamcrest", "hamcrest-library", hamcrestVersion)
 }
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt
index 3173028f8..c8502a6f7 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/FeatureSet.kt
@@ -45,7 +45,7 @@ class FeatureSet internal constructor(
     fun build(vararg features: QuasselFeature) = FeatureSet(features.toSet())
     fun build(features: Set<QuasselFeature>) = FeatureSet(features)
     fun all() = build(*QuasselFeature.values())
-    fun empty() = build()
+    fun none() = build()
 
     private fun parseFeatures(features: LegacyFeatures) =
       features.map(LegacyFeature::feature).toSet()
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt
index eefd14805..a1585029d 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/features/LegacyFeature.kt
@@ -21,6 +21,8 @@ package de.kuschku.libquassel.protocol.features
 
 import de.kuschku.bitflags.Flag
 import de.kuschku.bitflags.Flags
+import de.kuschku.bitflags.toEnumSet
+import java.util.*
 
 /**
  * A list of features that are optional in core and/or client, but need runtime checking
@@ -68,7 +70,7 @@ enum class LegacyFeature(
 
     private val values = values().associateBy(LegacyFeature::value)
     override fun get(value: UInt) = values[value]
-    override fun all() = values.values
+    override val all: LegacyFeatures = values.values.toEnumSet()
   }
 }
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializer.kt
index 450b01061..a727ed77e 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializer.kt
@@ -32,7 +32,7 @@ object DateTimeSerializer : QtSerializer<Temporal> {
   override val javaType: Class<out Temporal> = Temporal::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: Temporal, featureSet: FeatureSet) {
-    fun serialize(data: LocalDateTime, timeSpec: TimeSpec, offset: ZoneOffset? = null) {
+    fun serialize(data: LocalDateTime, timeSpec: TimeSpec, offset: ZoneOffset?) {
       DateSerializer.serialize(buffer, data.toLocalDate(), featureSet)
       TimeSerializer.serialize(buffer, data.toLocalTime(), featureSet)
       ByteSerializer.serialize(buffer, timeSpec.value, featureSet)
@@ -43,13 +43,13 @@ object DateTimeSerializer : QtSerializer<Temporal> {
 
     when (data) {
       is LocalDateTime ->
-        serialize(data, TimeSpec.LocalUnknown)
+        serialize(data, TimeSpec.LocalUnknown, null)
       is OffsetDateTime ->
         serialize(data.toLocalDateTime(), TimeSpec.OffsetFromUTC, data.offset)
       is ZonedDateTime ->
         serialize(data.toLocalDateTime(), TimeSpec.OffsetFromUTC, data.offset)
       is Instant ->
-        serialize(data.atOffset(ZoneOffset.UTC).toLocalDateTime(), TimeSpec.OffsetFromUTC)
+        serialize(data.atOffset(ZoneOffset.UTC).toLocalDateTime(), TimeSpec.UTC, null)
       else ->
         throw IllegalArgumentException("Unsupported Format: ${data::class.java.canonicalName}")
     }
@@ -66,12 +66,10 @@ object DateTimeSerializer : QtSerializer<Temporal> {
       TimeSpec.LocalUnknown,
       TimeSpec.LocalDST ->
         localDateTime
-          .atZone(ZoneId.systemDefault())
       TimeSpec.OffsetFromUTC ->
         localDateTime
           .atOffset(ZoneOffset.ofTotalSeconds(
             IntSerializer.deserialize(buffer, featureSet)))
-          .toInstant()
       TimeSpec.UTC ->
         localDateTime
           .atOffset(ZoneOffset.UTC)
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializer.kt
new file mode 100644
index 000000000..6217ea633
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializer.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.variant.QtType
+import java.nio.ByteBuffer
+
+object DoubleSerializer : QtSerializer<Double> {
+  override val qtType: QtType = QtType.Double
+  override val javaType: Class<Double> = Double::class.java
+
+  override fun serialize(buffer: ChainedByteBuffer, data: Double, featureSet: FeatureSet) {
+    buffer.putDouble(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Double {
+    return buffer.getDouble()
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializer.kt
new file mode 100644
index 000000000..9cfaf16be
--- /dev/null
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializer.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.variant.QtType
+import java.nio.ByteBuffer
+
+object FloatSerializer : QtSerializer<Float> {
+  override val qtType: QtType = QtType.Float
+  override val javaType: Class<Float> = Float::class.java
+
+  override fun serialize(buffer: ChainedByteBuffer, data: Float, featureSet: FeatureSet) {
+    buffer.putFloat(data)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): Float {
+    return buffer.getFloat()
+  }
+}
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializer.kt
index ebc0427e2..4eaf85cad 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializer.kt
@@ -65,7 +65,7 @@ object MessageSerializer : QuasselSerializer<Message> {
         Instant.ofEpochSecond(IntSerializer.deserialize(buffer, featureSet).toLong()),
       type = MessageType.of(UIntSerializer.deserialize(buffer, featureSet)),
       flag = MessageFlag.of(
-        UByteSerializer.deserialize(buffer, featureSet).toUInt() and 0xffu
+        UByteSerializer.deserialize(buffer, featureSet).toUInt()
       ),
       bufferInfo = BufferInfoSerializer.deserialize(buffer, featureSet),
       sender = StringSerializerUtf8.deserialize(buffer, featureSet) ?: "",
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt
index b3ec8e15e..b1caf13ea 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializer.kt
@@ -21,7 +21,7 @@ package de.kuschku.libquassel.protocol.serializers.primitive
 
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
-import de.kuschku.libquassel.protocol.serializers.*
+import de.kuschku.libquassel.protocol.serializers.NoSerializerForTypeException
 import de.kuschku.libquassel.protocol.variant.QVariant
 import de.kuschku.libquassel.protocol.variant.QVariant_
 import de.kuschku.libquassel.protocol.variant.QtType
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt
index 89b05757c..a8001289c 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/Serializers.kt
@@ -29,13 +29,20 @@ object Serializers {
   private val qtSerializers = setOf<QtSerializer<*>>(
     VoidSerializer,
     BoolSerializer,
+
+    ByteSerializer,
+    UByteSerializer,
+    ShortSerializer,
+    UShortSerializer,
     IntSerializer,
     UIntSerializer,
+    LongSerializer,
+    ULongSerializer,
 
-    QCharSerializer,
-    QVariantMapSerializer,
-    QVariantListSerializer,
+    FloatSerializer,
+    DoubleSerializer,
 
+    QCharSerializer,
     StringSerializerUtf16,
     QStringListSerializer,
     ByteBufferSerializer,
@@ -44,16 +51,12 @@ object Serializers {
     TimeSerializer,
     DateTimeSerializer,
 
-    LongSerializer,
-    ShortSerializer,
-    ByteSerializer,
-    ULongSerializer,
-
-    UShortSerializer,
-    UByteSerializer,
-
     QVariantSerializer,
+    QVariantListSerializer,
+    QVariantMapSerializer,
+  ).associateBy(QtSerializer<*>::qtType)
 
+  private val quasselSerializers = listOf<QuasselSerializer<*>>(
     BufferIdSerializer,
     BufferInfoSerializer,
     //DccConfigIpDetectionModeSerializer,
@@ -69,9 +72,6 @@ object Serializers {
     //NetworkServerSerializer,
     //QHostAddressSerializer,
     PeerPtrSerializer,
-  ).associateBy(QtSerializer<*>::qtType)
-
-  private val quasselSerializers = listOf<QuasselSerializer<*>>(
   ).associateBy(QuasselSerializer<*>::quasselType)
 
   operator fun get(type: QtType) = qtSerializers[type]
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt
index 0ad2b2cdd..7d2dae33f 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializer.kt
@@ -30,7 +30,7 @@ import kotlin.concurrent.getOrSet
 
 abstract class StringSerializer(
   private val charset: Charset,
-  private val nullLimited: Boolean = false,
+  private val nullLimited: Boolean,
 ) : QtSerializer<String?> {
   override val qtType = QtType.QString
   override val javaType: Class<out String> = String::class.java
@@ -38,10 +38,8 @@ abstract class StringSerializer(
   private val encoderLocal = ThreadLocal<StringEncoder>()
   private fun encoder() = encoderLocal.getOrSet { StringEncoder(charset) }
 
-  @Suppress("NOTHING_TO_INLINE")
-  private inline fun addNullBytes(before: Int) = if (nullLimited) before + 1 else before
-  @Suppress("NOTHING_TO_INLINE")
-  private inline fun removeNullBytes(before: Int) = if (nullLimited) before - 1 else before
+  private fun addNullBytes(before: Int) = if (nullLimited) before + 1 else before
+  private fun removeNullBytes(before: Int) = if (nullLimited) before - 1 else before
 
   override fun serialize(buffer: ChainedByteBuffer, data: String?, featureSet: FeatureSet) {
     if (data == null) {
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt
index 65114cd0c..3cbefd468 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf16.kt
@@ -19,4 +19,4 @@
 
 package de.kuschku.libquassel.protocol.serializers.primitive
 
-object StringSerializerUtf16 : StringSerializer(Charsets.UTF_16BE)
+object StringSerializerUtf16 : StringSerializer(Charsets.UTF_16BE, false)
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt
index 23c93e976..4d05c3d01 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerUtf8.kt
@@ -19,4 +19,4 @@
 
 package de.kuschku.libquassel.protocol.serializers.primitive
 
-object StringSerializerUtf8 : StringSerializer(Charsets.UTF_8)
+object StringSerializerUtf8 : StringSerializer(Charsets.UTF_8, false)
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/TimeSerializer.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/TimeSerializer.kt
index a93e623c9..c608474f8 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/TimeSerializer.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/serializers/primitive/TimeSerializer.kt
@@ -30,12 +30,12 @@ object TimeSerializer : QtSerializer<LocalTime> {
   override val javaType: Class<out LocalTime> = LocalTime::class.java
 
   override fun serialize(buffer: ChainedByteBuffer, data: LocalTime, featureSet: FeatureSet) {
-    val millisecondOfDay = (data.toNanoOfDay() / 1000).toInt()
+    val millisecondOfDay = (data.toNanoOfDay() / 1_000_000).toInt()
     IntSerializer.serialize(buffer, millisecondOfDay, featureSet)
   }
 
   override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): LocalTime {
     val millisecondOfDay = IntSerializer.deserialize(buffer, featureSet).toLong()
-    return LocalTime.ofNanoOfDay(millisecondOfDay * 1000)
+    return LocalTime.ofNanoOfDay(millisecondOfDay * 1_000_000)
   }
 }
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferActivity.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferActivity.kt
index 8bd2dbe28..bbefa87b7 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferActivity.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferActivity.kt
@@ -21,6 +21,8 @@ package de.kuschku.libquassel.protocol.types
 
 import de.kuschku.bitflags.Flag
 import de.kuschku.bitflags.Flags
+import de.kuschku.bitflags.toEnumSet
+import de.kuschku.libquassel.protocol.features.LegacyFeature
 
 enum class BufferActivity(
   override val value: UInt,
@@ -33,7 +35,7 @@ enum class BufferActivity(
   companion object : Flags<UInt, BufferActivity> {
     private val values = values().associateBy(BufferActivity::value)
     override fun get(value: UInt) = values[value]
-    override fun all() = values.values
+    override val all: BufferActivities = values.values.toEnumSet()
   }
 }
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferInfo.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferInfo.kt
index 91e0726c2..a3c516466 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferInfo.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferInfo.kt
@@ -19,12 +19,12 @@
 
 package de.kuschku.libquassel.protocol.types
 
-import de.kuschku.bitflags.of
+import de.kuschku.bitflags.none
 
 data class BufferInfo(
   val bufferId: BufferId = BufferId(-1),
   val networkId: NetworkId = NetworkId(-1),
-  val type: BufferTypes = BufferType.of(),
+  val type: BufferTypes = BufferType.none(),
   val groupId: Int = -1,
   val bufferName: String? = null,
 )
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferType.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferType.kt
index ae609475c..44d0ca830 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferType.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/BufferType.kt
@@ -21,6 +21,7 @@ package de.kuschku.libquassel.protocol.types
 
 import de.kuschku.bitflags.Flag
 import de.kuschku.bitflags.Flags
+import de.kuschku.bitflags.toEnumSet
 
 enum class BufferType(
   override val value: UShort,
@@ -34,7 +35,7 @@ enum class BufferType(
   companion object : Flags<UShort, BufferType> {
     private val values = values().associateBy(BufferType::value)
     override fun get(value: UShort) = values[value]
-    override fun all() = values.values
+    override val all: BufferTypes = values.values.toEnumSet()
   }
 }
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageFlag.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageFlag.kt
index da916d387..8d45bfed5 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageFlag.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageFlag.kt
@@ -21,6 +21,7 @@ package de.kuschku.libquassel.protocol.types
 
 import de.kuschku.bitflags.Flag
 import de.kuschku.bitflags.Flags
+import de.kuschku.bitflags.toEnumSet
 
 enum class MessageFlag(
   override val value: UInt,
@@ -34,7 +35,7 @@ enum class MessageFlag(
   companion object : Flags<UInt, MessageFlag> {
     private val values = values().associateBy(MessageFlag::value)
     override fun get(value: UInt) = values[value]
-    override fun all() = values.values
+    override val all: MessageFlags = values.values.toEnumSet()
   }
 }
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageType.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageType.kt
index a5afb2c50..e0882d595 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageType.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/types/MessageType.kt
@@ -21,6 +21,7 @@ package de.kuschku.libquassel.protocol.types
 
 import de.kuschku.bitflags.Flag
 import de.kuschku.bitflags.Flags
+import de.kuschku.bitflags.toEnumSet
 
 enum class MessageType(
   override val value: UInt,
@@ -48,7 +49,7 @@ enum class MessageType(
   companion object : Flags<UInt, MessageType> {
     private val values = values().associateBy(MessageType::value)
     override fun get(value: UInt) = values[value]
-    override fun all() = values.values
+    override val all: MessageTypes = values.values.toEnumSet()
   }
 }
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt
index 6b6c01114..93cb9358a 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QVariant.kt
@@ -38,42 +38,10 @@ sealed class QVariant<T> constructor(
   open val serializer: QtSerializer<T>,
 ) {
   class Typed<T> internal constructor(data: T, serializer: QtSerializer<T>) :
-    QVariant<T>(data, serializer) {
-    override fun equals(other: Any?): Boolean {
-      if (this === other) return true
-      if (other !is Typed<*>) return false
-
-      if (data != other.data) return false
-      if (serializer.qtType != other.serializer.qtType) return false
-
-      return true
-    }
-
-    override fun hashCode(): Int {
-      var result = data?.hashCode() ?: 0
-      result = 31 * result + serializer.qtType.hashCode()
-      return result
-    }
-  }
+    QVariant<T>(data, serializer)
 
   class Custom<T> internal constructor(data: T, override val serializer: QuasselSerializer<T>) :
-    QVariant<T>(data, serializer) {
-    override fun equals(other: Any?): Boolean {
-      if (this === other) return true
-      if (other !is Custom<*>) return false
-
-      if (data != other.data) return false
-      if (serializer.quasselType != other.serializer.quasselType) return false
-
-      return true
-    }
-
-    override fun hashCode(): Int {
-      var result = data?.hashCode() ?: 0
-      result = 31 * result + serializer.quasselType.hashCode()
-      return result
-    }
-  }
+    QVariant<T>(data, serializer)
 
   fun value(): T = data
 
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QtType.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QtType.kt
index 3f9ef4037..c0b682a40 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QtType.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QtType.kt
@@ -105,8 +105,7 @@ enum class QtType(val id: kotlin.Int) {
   QVariant(138),//, VariantSerializer),
 
   User(256),
-  UserType(127),
-  LastType(-1);
+  UserType(127);
 
   val serializableName =
     if (name.startsWith("Q")) name
diff --git a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QuasselType.kt b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QuasselType.kt
index 77e581685..64f13fa4e 100644
--- a/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QuasselType.kt
+++ b/protocol/src/main/java/de/kuschku/libquassel/protocol/variant/QuasselType.kt
@@ -37,7 +37,8 @@ enum class QuasselType(
   NetworkInfo("NetworkInfo"),
   NetworkServer("Network::Server"),
   QHostAddress("QHostAddress"),
-  PeerPtr("PeerPtr");
+  PeerPtr("PeerPtr"),
+  Unknown("");
 
   companion object {
     private val values = values().associateBy(QuasselType::typeName)
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt
index c03ae034c..fca778612 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/handshake/ClientInitSerializerTest.kt
@@ -16,46 +16,37 @@
  * 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.serializers.handshake
 
+import de.kuschku.bitflags.none
 import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.features.LegacyFeature
 import de.kuschku.libquassel.protocol.messages.handshake.ClientInit
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testHandshakeSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testHandshakeSerializerEncoded
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.handshakeSerializerTest
+import org.junit.jupiter.api.Test
 
 class ClientInitSerializerTest {
   @Test
-  fun testSimple() {
-    val value = ClientInit(
+  fun testSimple() = handshakeSerializerTest(
+    ClientInitSerializer,
+    ClientInit(
       clientVersion = "Quasseldroid test",
       buildDate = "Never",
-      clientFeatures = flags(),
+      clientFeatures = LegacyFeature.none(),
       featureList = emptyList()
-    )
-
-    testHandshakeSerializerDirect(ClientInitSerializer, value)
-    testHandshakeSerializerEncoded(ClientInitSerializer, value)
-
-    // @formatter:off
-    testDeserialize(ClientInitSerializer, value, byteBufferOf(0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x07u, 0x4Du, 0x73u, 0x67u, 0x54u, 0x79u, 0x70u, 0x65u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x14u, 0x00u, 0x43u, 0x00u, 0x6Cu, 0x00u, 0x69u, 0x00u, 0x65u, 0x00u, 0x6Eu, 0x00u, 0x74u, 0x00u, 0x49u, 0x00u, 0x6Eu, 0x00u, 0x69u, 0x00u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Du, 0x43u, 0x6Cu, 0x69u, 0x65u, 0x6Eu, 0x74u, 0x56u, 0x65u, 0x72u, 0x73u, 0x69u, 0x6Fu, 0x6Eu, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x22u, 0x00u, 0x51u, 0x00u, 0x75u, 0x00u, 0x61u, 0x00u, 0x73u, 0x00u, 0x73u, 0x00u, 0x65u, 0x00u, 0x6Cu, 0x00u, 0x64u, 0x00u, 0x72u, 0x00u, 0x6Fu, 0x00u, 0x69u, 0x00u, 0x64u, 0x00u, 0x20u, 0x00u, 0x74u, 0x00u, 0x65u, 0x00u, 0x73u, 0x00u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x43u, 0x6Cu, 0x69u, 0x65u, 0x6Eu, 0x74u, 0x44u, 0x61u, 0x74u, 0x65u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x4Eu, 0x00u, 0x65u, 0x00u, 0x76u, 0x00u, 0x65u, 0x00u, 0x72u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x08u, 0x46u, 0x65u, 0x61u, 0x74u, 0x75u, 0x72u, 0x65u, 0x73u, 0x00u, 0x00u, 0x00u, 0x03u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Bu, 0x46u, 0x65u, 0x61u, 0x74u, 0x75u, 0x72u, 0x65u, 0x4Cu, 0x69u, 0x73u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Bu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u))
-    // @formatter:on
-  }
+    ),
+    byteBufferOf(0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x07u, 0x4Du, 0x73u, 0x67u, 0x54u, 0x79u, 0x70u, 0x65u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x14u, 0x00u, 0x43u, 0x00u, 0x6Cu, 0x00u, 0x69u, 0x00u, 0x65u, 0x00u, 0x6Eu, 0x00u, 0x74u, 0x00u, 0x49u, 0x00u, 0x6Eu, 0x00u, 0x69u, 0x00u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Du, 0x43u, 0x6Cu, 0x69u, 0x65u, 0x6Eu, 0x74u, 0x56u, 0x65u, 0x72u, 0x73u, 0x69u, 0x6Fu, 0x6Eu, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x22u, 0x00u, 0x51u, 0x00u, 0x75u, 0x00u, 0x61u, 0x00u, 0x73u, 0x00u, 0x73u, 0x00u, 0x65u, 0x00u, 0x6Cu, 0x00u, 0x64u, 0x00u, 0x72u, 0x00u, 0x6Fu, 0x00u, 0x69u, 0x00u, 0x64u, 0x00u, 0x20u, 0x00u, 0x74u, 0x00u, 0x65u, 0x00u, 0x73u, 0x00u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x43u, 0x6Cu, 0x69u, 0x65u, 0x6Eu, 0x74u, 0x44u, 0x61u, 0x74u, 0x65u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x4Eu, 0x00u, 0x65u, 0x00u, 0x76u, 0x00u, 0x65u, 0x00u, 0x72u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x08u, 0x46u, 0x65u, 0x61u, 0x74u, 0x75u, 0x72u, 0x65u, 0x73u, 0x00u, 0x00u, 0x00u, 0x03u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Cu, 0x00u, 0x00u, 0x00u, 0x00u, 0x0Bu, 0x46u, 0x65u, 0x61u, 0x74u, 0x75u, 0x72u, 0x65u, 0x4Cu, 0x69u, 0x73u, 0x74u, 0x00u, 0x00u, 0x00u, 0x0Bu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
+  )
 
   @Test
-  fun testRealistic() {
-    val features = FeatureSet.all()
-    val value = ClientInit(
+  fun testRealistic() = handshakeSerializerTest(
+    ClientInitSerializer,
+    ClientInit(
       clientVersion = "Quasseldroid <a href=\"https://git.kuschku.de/justJanne/QuasselDroid-ng/commit/b622ad63056b6054b06e09f8e1f1ef2b0c3aaf9a\">v1.3.3</a>",
       buildDate = "2020-04-27T22:21:17Z",
-      clientFeatures = features.legacyFeatures(),
-      featureList = features.featureList()
+      clientFeatures = FeatureSet.all().legacyFeatures(),
+      featureList = FeatureSet.all().featureList()
     )
-
-    testHandshakeSerializerDirect(ClientInitSerializer, value)
-    testHandshakeSerializerEncoded(ClientInitSerializer, value)
-  }
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt
index 64f50e7af..c6dfa9a3c 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BoolSerializerTest.kt
@@ -16,31 +16,24 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 
 class BoolSerializerTest {
   @Test
-  fun testTrue() {
-    testQtSerializerDirect(BoolSerializer, true)
-    testQtSerializerVariant(BoolSerializer, true)
-    // @formatter:off
-    testDeserialize(BoolSerializer, true, byteBufferOf(1))
-    // @formatter:on
-  }
+  fun testTrue() = qtSerializerTest(
+    BoolSerializer,
+    true,
+    byteBufferOf(1)
+  )
 
   @Test
-  fun testFalse() {
-    testQtSerializerDirect(BoolSerializer, false)
-    testQtSerializerVariant(BoolSerializer, false)
-    // @formatter:off
-    testDeserialize(BoolSerializer, false, byteBufferOf(0))
-    // @formatter:on
-  }
+  fun testFalse() = qtSerializerTest(
+    BoolSerializer,
+    false,
+    byteBufferOf(0)
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BufferInfoSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BufferInfoSerializerTest.kt
new file mode 100644
index 000000000..f32ae8ae1
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/BufferInfoSerializerTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.bitflags.none
+import de.kuschku.bitflags.validValues
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.quasselSerializerTest
+import de.kuschku.libquassel.protocol.types.BufferId
+import de.kuschku.libquassel.protocol.types.BufferInfo
+import de.kuschku.libquassel.protocol.types.BufferType
+import de.kuschku.libquassel.protocol.types.NetworkId
+import org.junit.jupiter.api.Test
+
+class BufferInfoSerializerTest {
+  @Test
+  fun testBaseCase() = quasselSerializerTest(
+    BufferInfoSerializer,
+    BufferInfo(
+      BufferId(-1),
+      NetworkId(-1),
+      BufferType.none(),
+      -1,
+      ""
+    ),
+    byteBufferOf(0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testNormal() = quasselSerializerTest(
+    BufferInfoSerializer,
+    BufferInfo(
+      BufferId.MAX_VALUE,
+      NetworkId.MAX_VALUE,
+      BufferType.validValues(),
+      Int.MAX_VALUE,
+      "äẞ\u0000\uFFFF"
+    ),
+    byteBufferOf(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)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt
index fa706ec99..e109746fc 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteBufferSerializerTest.kt
@@ -16,31 +16,27 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
 import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 
 class ByteBufferSerializerTest {
   @Test
-  fun testBaseCase() {
-    val value = byteBufferOf(0)
-    testQtSerializerDirect(ByteBufferSerializer, value, ByteBufferMatcher(value))
-    // @formatter:off
-    testDeserialize(ByteBufferSerializer, ByteBufferMatcher(value), byteBufferOf(0, 0, 0, 1, 0))
-    // @formatter:on
-  }
+  fun testBaseCase() = qtSerializerTest(
+    ByteBufferSerializer,
+    byteBufferOf(0),
+    byteBufferOf(0, 0, 0, 1, 0),
+    ::ByteBufferMatcher
+  )
 
   @Test
-  fun testNormal() {
-    val value = byteBufferOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
-    testQtSerializerDirect(ByteBufferSerializer, value, ByteBufferMatcher(value))
-    // @formatter:off
-    testDeserialize(ByteBufferSerializer, ByteBufferMatcher(value), byteBufferOf(0, 0, 0, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9))
-    // @formatter:on
-  }
+  fun testNormal() = qtSerializerTest(
+    ByteBufferSerializer,
+    byteBufferOf(1, 2, 3, 4, 5, 6, 7, 8, 9),
+    byteBufferOf(0, 0, 0, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9),
+    ::ByteBufferMatcher
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt
index 600952e1a..acb9c9960 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ByteSerializerTest.kt
@@ -16,55 +16,39 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 import kotlin.experimental.inv
 
 class ByteSerializerTest {
   @Test
-  fun testZero() {
-    val value = 0.toByte()
-    testQtSerializerDirect(ByteSerializer, value)
-    testQtSerializerVariant(ByteSerializer, value)
-    // @formatter:off
-    testDeserialize(ByteSerializer, value, byteBufferOf(0))
-    // @formatter:on
-  }
+  fun testZero() = qtSerializerTest(
+    ByteSerializer,
+    0.toByte(),
+    byteBufferOf(0)
+  )
 
   @Test
-  fun testMinimal() {
-    val value = Byte.MIN_VALUE
-    testQtSerializerDirect(ByteSerializer, value)
-    testQtSerializerVariant(ByteSerializer, value)
-    // @formatter:off
-    testDeserialize(ByteSerializer, value, byteBufferOf(-128))
-    // @formatter:on
-  }
+  fun testMinimal() = qtSerializerTest(
+    ByteSerializer,
+    Byte.MIN_VALUE,
+    byteBufferOf(-128)
+  )
 
   @Test
-  fun testMaximal() {
-    val value = Byte.MAX_VALUE
-    testQtSerializerDirect(ByteSerializer, value)
-    testQtSerializerVariant(ByteSerializer, value)
-    // @formatter:off
-    testDeserialize(ByteSerializer, value, byteBufferOf(127))
-    // @formatter:on
-  }
+  fun testMaximal() = qtSerializerTest(
+    ByteSerializer,
+    Byte.MAX_VALUE,
+    byteBufferOf(127)
+  )
 
   @Test
-  fun testAllOnes() {
-    val value = 0.toByte().inv()
-
-    testQtSerializerDirect(ByteSerializer, value)
-    testQtSerializerVariant(ByteSerializer, value)
-    // @formatter:off
-    testDeserialize(ByteSerializer, value, byteBufferOf(-1))
-    // @formatter:on
-  }
+  fun testAllOnes() = qtSerializerTest(
+    ByteSerializer,
+    0.toByte().inv(),
+    byteBufferOf(-1)
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializerTest.kt
new file mode 100644
index 000000000..1f2352a43
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DateTimeSerializerTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.matchers.TemporalMatcher
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import org.threeten.bp.*
+import org.threeten.bp.chrono.JapaneseDate
+
+class DateTimeSerializerTest {
+  private val serializer = DateTimeSerializer
+
+  @Test
+  fun testEpoch() = qtSerializerTest(
+    DateTimeSerializer,
+    Instant.EPOCH,
+    byteBufferOf(0, 37, 61, -116, 0, 0, 0, 0, 2),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testEpochAtTimezone() = qtSerializerTest(
+    DateTimeSerializer,
+    Instant.EPOCH.atOffset(ZoneOffset.ofTotalSeconds(1234)),
+    byteBufferOf(0x00u, 0x25u, 0x3Du, 0x8Cu, 0x00u, 0x12u, 0xD4u, 0x50u, 0x03u, 0x00u, 0x00u, 0x04u, 0xD2u),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testEpochByCalendarAtTimezone() = qtSerializerTest(
+    DateTimeSerializer,
+    LocalDateTime
+      .of(1970, 1, 1, 0, 0)
+      .atZone(ZoneId.of("Europe/Berlin"))
+      .toInstant(),
+    byteBufferOf(0, 37, 61, -117, 4, -17, 109, -128, 2),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testNormalCase() = qtSerializerTest(
+    DateTimeSerializer,
+    LocalDateTime
+      .of(2019, Month.JANUARY, 15, 20, 25)
+      .atZone(ZoneId.of("Europe/Berlin"))
+      .toInstant(),
+    byteBufferOf(0, 37, -125, -125, 4, 42, -106, -32, 2),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testLocalDateTime() = qtSerializerTest(
+    DateTimeSerializer,
+    LocalDateTime
+      .of(2019, Month.JANUARY, 15, 20, 25),
+    byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0xFFu),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testZonedDateTime() = qtSerializerTest(
+    DateTimeSerializer,
+    LocalDateTime
+      .of(2019, Month.JANUARY, 15, 20, 25)
+      .atZone(ZoneId.systemDefault()),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testUnknownDateTime() = qtSerializerTest(
+    DateTimeSerializer,
+    LocalDateTime
+      .of(2019, Month.JANUARY, 15, 20, 25),
+    byteBufferOf(0x00u, 0x25u, 0x83u, 0x83u, 0x04u, 0x61u, 0x85u, 0x60u, 0x07u),
+    matcher = ::TemporalMatcher
+  )
+
+  @Test
+  fun testOldJavaDate() {
+    assertThrows<IllegalArgumentException>("Unsupported Format: org.threeten.bp.chrono.JapaneseDate") {
+      qtSerializerTest(
+        DateTimeSerializer,
+        JapaneseDate.now(),
+        matcher = ::TemporalMatcher
+      )
+    }
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializerTest.kt
new file mode 100644
index 000000000..5b6c41938
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/DoubleSerializerTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+
+class DoubleSerializerTest {
+  @Test
+  fun testZero() = qtSerializerTest(
+    DoubleSerializer,
+    0.0,
+    byteBufferOf(0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testMinimal() = qtSerializerTest(
+    DoubleSerializer,
+    Double.MIN_VALUE,
+    byteBufferOf(0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x01u)
+  )
+
+  @Test
+  fun testMaximal() = qtSerializerTest(
+    DoubleSerializer,
+    Double.MAX_VALUE,
+    byteBufferOf(0x7Fu, 0xEFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu)
+  )
+
+  @Test
+  fun testInfinityPositive() = qtSerializerTest(
+    DoubleSerializer,
+    Double.POSITIVE_INFINITY,
+    byteBufferOf(0x7Fu, 0xF0u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testInfinityNegative() = qtSerializerTest(
+    DoubleSerializer,
+    Double.NEGATIVE_INFINITY,
+    byteBufferOf(0xFFu, 0xF0u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testNotANumber() = qtSerializerTest(
+    DoubleSerializer,
+    Double.NaN,
+    byteBufferOf(0x7Fu, 0xF8u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializerTest.kt
new file mode 100644
index 000000000..ea12027a0
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/FloatSerializerTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+
+class FloatSerializerTest {
+  @Test
+  fun testZero() = qtSerializerTest(
+    FloatSerializer,
+    0f,
+    byteBufferOf(0x00u, 0x00u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testMinimal() = qtSerializerTest(
+    FloatSerializer,
+    Float.MIN_VALUE,
+    byteBufferOf(0x00u, 0x00u, 0x00u, 0x01u)
+  )
+
+  @Test
+  fun testMaximal() = qtSerializerTest(
+    FloatSerializer,
+    Float.MAX_VALUE,
+    byteBufferOf(0x7Fu, 0x7Fu, 0xFFu, 0xFFu)
+  )
+
+  @Test
+  fun testInfinityPositive() = qtSerializerTest(
+    FloatSerializer,
+    Float.POSITIVE_INFINITY,
+    byteBufferOf(0x7Fu, 0x80u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testInfinityNegative() = qtSerializerTest(
+    FloatSerializer,
+    Float.NEGATIVE_INFINITY,
+    byteBufferOf(0xFFu, 0x80u, 0x00u, 0x00u)
+  )
+
+  @Test
+  fun testNotANumber() = qtSerializerTest(
+    FloatSerializer,
+    Float.NaN,
+    byteBufferOf(0x7Fu, 0xC0u, 0x00u, 0x00u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt
index 26161a3c6..019f3245e 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/IntSerializerTest.kt
@@ -16,54 +16,38 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 
 class IntSerializerTest {
   @Test
-  fun testZero() {
-    val value = 0
-    testQtSerializerDirect(IntSerializer, value)
-    testQtSerializerVariant(IntSerializer, value)
-    // @formatter:off
-    testDeserialize(IntSerializer, value, byteBufferOf(0, 0, 0, 0))
-    // @formatter:on
-  }
+  fun testZero() = qtSerializerTest(
+    IntSerializer,
+    0,
+    byteBufferOf(0, 0, 0, 0)
+  )
 
   @Test
-  fun testMinimal() {
-    val value = Int.MIN_VALUE
-    testQtSerializerDirect(IntSerializer, value)
-    testQtSerializerVariant(IntSerializer, value)
-    // @formatter:off
-    testDeserialize(IntSerializer, value, byteBufferOf(-128, 0, 0, 0))
-    // @formatter:on
-  }
+  fun testMinimal() = qtSerializerTest(
+    IntSerializer,
+    Int.MIN_VALUE,
+    byteBufferOf(-128, 0, 0, 0)
+  )
 
   @Test
-  fun testMaximal() {
-    val value = Int.MAX_VALUE
-    testQtSerializerDirect(IntSerializer, value)
-    testQtSerializerVariant(IntSerializer, value)
-    // @formatter:off
-    testDeserialize(IntSerializer, value, byteBufferOf(127, -1, -1, -1))
-    // @formatter:on
-  }
+  fun testMaximal() = qtSerializerTest(
+    IntSerializer,
+    Int.MAX_VALUE,
+    byteBufferOf(127, -1, -1, -1)
+  )
 
   @Test
-  fun testAllOnes() {
-    val value = 0.inv()
-
-    testQtSerializerDirect(IntSerializer, value)
-    testQtSerializerVariant(IntSerializer, value)
-    // @formatter:off
-    testDeserialize(IntSerializer, value, byteBufferOf(-1, -1, -1, -1))
-    // @formatter:on
-  }
+  fun testAllOnes() = qtSerializerTest(
+    IntSerializer,
+    0.inv(),
+    byteBufferOf(-1, -1, -1, -1)
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt
index 48835fdc6..f6cc0729c 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/LongSerializerTest.kt
@@ -16,54 +16,38 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 
 class LongSerializerTest {
   @Test
-  fun testZero() {
-    val value = 0.toLong()
-    testQtSerializerDirect(LongSerializer, value)
-    testQtSerializerVariant(LongSerializer, value)
-    // @formatter:off
-    testDeserialize(LongSerializer, value, byteBufferOf(0, 0, 0, 0, 0, 0, 0, 0))
-    // @formatter:on
-  }
+  fun testZero() = qtSerializerTest(
+    LongSerializer,
+    0L,
+    byteBufferOf(0, 0, 0, 0, 0, 0, 0, 0)
+  )
 
   @Test
-  fun testMinimal() {
-    val value = Long.MIN_VALUE
-    testQtSerializerDirect(LongSerializer, value)
-    testQtSerializerVariant(LongSerializer, value)
-    // @formatter:off
-    testDeserialize(LongSerializer, value, byteBufferOf(-128, 0, 0, 0, 0, 0, 0, 0))
-    // @formatter:on
-  }
+  fun testMinimal() = qtSerializerTest(
+    LongSerializer,
+    Long.MIN_VALUE,
+    byteBufferOf(-128, 0, 0, 0, 0, 0, 0, 0)
+  )
 
   @Test
-  fun testMaximal() {
-    val value = Long.MAX_VALUE
-    testQtSerializerDirect(LongSerializer, value)
-    testQtSerializerVariant(LongSerializer, value)
-    // @formatter:off
-    testDeserialize(LongSerializer, value, byteBufferOf(127, -1, -1, -1, -1, -1, -1, -1))
-    // @formatter:on
-  }
+  fun testMaximal() = qtSerializerTest(
+    LongSerializer,
+    Long.MAX_VALUE,
+    byteBufferOf(127, -1, -1, -1, -1, -1, -1, -1)
+  )
 
   @Test
-  fun testAllOnes() {
-    val value = 0.toLong().inv()
-
-    testQtSerializerDirect(LongSerializer, value)
-    testQtSerializerVariant(LongSerializer, value)
-    // @formatter:off
-    testDeserialize(LongSerializer, value, byteBufferOf(-1, -1, -1, -1, -1, -1, -1, -1))
-    // @formatter:on
-  }
+  fun testAllOnes() = qtSerializerTest(
+    LongSerializer,
+    0L.inv(),
+    byteBufferOf(-1, -1, -1, -1, -1, -1, -1, -1)
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializerTest.kt
new file mode 100644
index 000000000..f07ad5f6c
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/MessageSerializerTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.bitflags.none
+import de.kuschku.bitflags.validValues
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.quasselSerializerTest
+import de.kuschku.libquassel.protocol.types.*
+import org.junit.jupiter.api.Test
+import org.threeten.bp.Instant
+
+class MessageSerializerTest {
+  @Test
+  fun testEmpty() = quasselSerializerTest(
+    MessageSerializer,
+    Message(
+      MsgId(-1),
+      Instant.EPOCH,
+      MessageType.none(),
+      MessageFlag.none(),
+      BufferInfo(
+        BufferId(-1),
+        NetworkId(-1),
+        BufferType.none(),
+        -1,
+        null
+      ),
+      "",
+      "",
+      "",
+      "",
+      ""
+    ),
+    byteBufferOf(
+      // MsgId
+      0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // Time
+      0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+      // Type
+      0x00u, 0x00u, 0x00u, 0x00u,
+      // Flags
+      0x00u,
+      // BufferId
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // NetworkId
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // BufferType
+      0x00u, 0x00u,
+      // GroupId
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // Buffername
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // Sender
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // Prefixes
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // RealName
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // AvatarUrl
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+      // Content
+      0xFFu, 0xFFu, 0xFFu, 0xFFu,
+    ),
+    deserializeFeatureSet = FeatureSet.all()
+  )
+
+  @Test
+  fun testBaseCase() = quasselSerializerTest(
+    MessageSerializer,
+    Message(
+      MsgId(-1),
+      Instant.EPOCH,
+      MessageType.none(),
+      MessageFlag.none(),
+      BufferInfo(
+        BufferId(-1),
+        NetworkId(-1),
+        BufferType.none(),
+        -1,
+        ""
+      ),
+      "",
+      "",
+      "",
+      "",
+      ""
+    ),
+    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()
+  )
+
+  @Test
+  fun testNormal() = quasselSerializerTest(
+    MessageSerializer,
+    Message(
+      MsgId(Int.MAX_VALUE.toLong()),
+      Instant.ofEpochMilli(1524601750000),
+      MessageType.validValues(),
+      MessageFlag.validValues(),
+      BufferInfo(
+        BufferId.MAX_VALUE,
+        NetworkId.MAX_VALUE,
+        BufferType.validValues(),
+        Int.MAX_VALUE,
+        "äẞ\u0000\uFFFF"
+      ),
+      "äẞ\u0000\uFFFF",
+      "",
+      "",
+      "",
+      "äẞ\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()
+  )
+
+  @Test
+  fun testExtreme() = quasselSerializerTest(
+    MessageSerializer,
+    Message(
+      MsgId.MAX_VALUE,
+      Instant.ofEpochMilli(Int.MAX_VALUE * 10000L),
+      MessageType.validValues(),
+      MessageFlag.validValues(),
+      BufferInfo(
+        BufferId.MAX_VALUE,
+        NetworkId.MAX_VALUE,
+        BufferType.validValues(),
+        Int.MAX_VALUE,
+        "äẞ\u0000\uFFFF"
+      ),
+      "äẞ\u0000\uFFFF",
+      "äẞ\u0000\uFFFF",
+      "äẞ\u0000\uFFFF",
+      "äẞ\u0000\uFFFF",
+      "äẞ\u0000\uFFFF"
+    ),
+    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(),
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt
index 8c6f8f88e..c95da6fd0 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QCharSerializerTest.kt
@@ -16,90 +16,70 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
 import de.kuschku.libquassel.protocol.testutil.matchers.BomMatcherChar
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 
 class QCharSerializerTest {
   @Test
-  fun testNull() {
-    val value = '\u0000'
-    testQtSerializerDirect(QCharSerializer, value)
-    testQtSerializerVariant(QCharSerializer, value)
-    // @formatter:off
-    testDeserialize(QCharSerializer, value, byteBufferOf(0, 0))
-    // @formatter:on
-  }
+  fun testNull() = qtSerializerTest(
+    QCharSerializer,
+    '\u0000',
+    byteBufferOf(0, 0),
+    ::BomMatcherChar,
+  )
 
   @Test
-  fun testAllOnes() {
-    val value = '\uFFFF'
-    testQtSerializerDirect(QCharSerializer, value)
-    testQtSerializerVariant(QCharSerializer, value)
-    // @formatter:off
-    testDeserialize(QCharSerializer, value, byteBufferOf(-1, -1))
-    // @formatter:on
-  }
+  fun testAllOnes() = qtSerializerTest(
+    QCharSerializer,
+    '\uFFFF',
+    byteBufferOf(-1, -1),
+    ::BomMatcherChar,
+  )
 
   @Test
-  fun testBOM1() {
-    val value = '\uFFFE'
-    testQtSerializerDirect(QCharSerializer, value)
-    testQtSerializerVariant(QCharSerializer, value)
-    // @formatter:off
-    testDeserialize(QCharSerializer, BomMatcherChar(value), byteBufferOf(-2, -1))
-    // @formatter:on
-  }
+  fun testBOM1() = qtSerializerTest(
+    QCharSerializer,
+    '\uFFFE',
+    byteBufferOf(-2, -1),
+    ::BomMatcherChar,
+  )
 
   @Test
-  fun testBOM2() {
-    val value = '\uFEFF'
-    testQtSerializerDirect(QCharSerializer, value)
-    testQtSerializerVariant(QCharSerializer, value)
-    // @formatter:off
-    testDeserialize(QCharSerializer, BomMatcherChar(value), byteBufferOf(-1, -2))
-    // @formatter:on
-  }
+  fun testBOM2() = qtSerializerTest(
+    QCharSerializer,
+    '\uFEFF',
+    byteBufferOf(-1, -2),
+    ::BomMatcherChar,
+  )
 
   @Test
   fun testAlphabet() {
-    for (index in 0..25) {
-      val value = 'a' + index
-      testQtSerializerDirect(QCharSerializer, value)
-      testQtSerializerVariant(QCharSerializer, value)
-      // @formatter:off
-      testDeserialize(QCharSerializer, value, byteBufferOf(0, (97 + index).toByte()))
-      // @formatter:on
-    }
-    for (index in 0..25) {
-      val value = 'A' + index
-      testQtSerializerDirect(QCharSerializer, value)
-      testQtSerializerVariant(QCharSerializer, value)
-      // @formatter:off
-      testDeserialize(QCharSerializer, value, byteBufferOf(0, (65 + index).toByte()))
-      // @formatter:on
-    }
-    for (index in 0..9) {
-      val value = '0' + index
-      testQtSerializerDirect(QCharSerializer, value)
-      testQtSerializerVariant(QCharSerializer, value)
-      // @formatter:off
-      testDeserialize(QCharSerializer, value, byteBufferOf(0, (48 + index).toByte()))
-      // @formatter:on
-    }
+    for (value in 'a'..'z') qtSerializerTest(
+      QCharSerializer,
+      value,
+      byteBufferOf(0, value.toByte())
+    )
+    for (value in 'A'..'Z') qtSerializerTest(
+      QCharSerializer,
+      value,
+      byteBufferOf(0, value.toByte())
+    )
+    for (value in '0'..'9') qtSerializerTest(
+      QCharSerializer,
+      value,
+      byteBufferOf(0, value.toByte())
+    )
   }
 
   @Test
   fun testAlphabetExtended() {
-    for (value in listOf('ä', 'ö', 'ü', 'ß', 'æ', 'ø', 'µ')) {
-      testQtSerializerDirect(QCharSerializer, value)
-      testQtSerializerVariant(QCharSerializer, value)
-    }
+    for (value in listOf('ä', 'ö', 'ü', 'ß', 'æ', 'ø', 'µ')) qtSerializerTest(
+      QCharSerializer,
+      value
+    )
   }
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializerTest.kt
new file mode 100644
index 000000000..88f60db3f
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/QVariantSerializerTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.serializers.NoSerializerForTypeException
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.deserialize
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import de.kuschku.libquassel.protocol.variant.QtType
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import kotlin.experimental.inv
+
+class QVariantSerializerTest {
+  @Test
+  fun testUnregisteredQtType() {
+    assertThrows<NoSerializerForTypeException> {
+      deserialize(
+        QVariantSerializer,
+        byteBufferOf(0x00u, 0x00u, 0x01u, 0x00u, 0x00u)
+      )
+    }
+  }
+
+  @Test
+  fun testUnknownQtType() {
+    assertThrows<NoSerializerForTypeException> {
+      deserialize(
+        QVariantSerializer,
+        byteBufferOf(0x00u, 0xFFu, 0x00u, 0x00u, 0x00u)
+      )
+    }
+  }
+
+  @Test
+  fun testUnregisteredQuasselType() {
+    assertThrows<NoSerializerForTypeException> {
+      deserialize(
+        QVariantSerializer,
+        byteBufferOf(
+          // QtType
+          0x00u, 0x00u, 0x00u, 0x7Fu,
+          // isNull
+          0x00u,
+          // QuasselType length
+          0x00u, 0x00u, 0x00u, 0x00u,
+        )
+      )
+    }
+  }
+
+  @Test
+  fun testUnknownQuasselType() {
+    assertThrows<NoSerializerForTypeException> {
+      deserialize(
+        QVariantSerializer,
+        byteBufferOf(
+          // QtType
+          0x00u, 0x00u, 0x00u, 0x7Fu,
+          // isNull
+          0x00u,
+          // QuasselType length
+          0x00u, 0x00u, 0x00u, 0x03u,
+          // "foo"
+          0x66u, 0x6fu, 0x6fu
+        )
+      )
+    }
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt
index ad6a53468..644a03b47 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ShortSerializerTest.kt
@@ -16,55 +16,39 @@
  * 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.serializers.primitive
 
 import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.testDeserialize
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Test
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
 import kotlin.experimental.inv
 
 class ShortSerializerTest {
   @Test
-  fun testZero() {
-    val value = 0.toShort()
-    testQtSerializerDirect(ShortSerializer, value)
-    testQtSerializerVariant(ShortSerializer, value)
-    // @formatter:off
-    testDeserialize(ShortSerializer, value, byteBufferOf(0, 0))
-    // @formatter:on
-  }
+  fun testZero() = qtSerializerTest(
+    ShortSerializer,
+    0.toShort(),
+    byteBufferOf(0, 0)
+  )
 
   @Test
-  fun testMinimal() {
-    val value = Short.MIN_VALUE
-    testQtSerializerDirect(ShortSerializer, value)
-    testQtSerializerVariant(ShortSerializer, value)
-    // @formatter:off
-    testDeserialize(ShortSerializer, value, byteBufferOf(-128, 0))
-    // @formatter:on
-  }
+  fun testMinimal() = qtSerializerTest(
+    ShortSerializer,
+    Short.MIN_VALUE,
+    byteBufferOf(-128, 0)
+  )
 
   @Test
-  fun testMaximal() {
-    val value = Short.MAX_VALUE
-    testQtSerializerDirect(ShortSerializer, value)
-    testQtSerializerVariant(ShortSerializer, value)
-    // @formatter:off
-    testDeserialize(ShortSerializer, value, byteBufferOf(127, -1))
-    // @formatter:on
-  }
+  fun testMaximal() = qtSerializerTest(
+    ShortSerializer,
+    Short.MAX_VALUE,
+    byteBufferOf(127, -1)
+  )
 
   @Test
-  fun testAllOnes() {
-    val value = 0.toShort().inv()
-
-    testQtSerializerDirect(ShortSerializer, value)
-    testQtSerializerVariant(ShortSerializer, value)
-    // @formatter:off
-    testDeserialize(ShortSerializer, value, byteBufferOf(-1, -1))
-    // @formatter:on
-  }
+  fun testAllOnes() = qtSerializerTest(
+    ShortSerializer,
+    0.toShort().inv(),
+    byteBufferOf(-1, -1)
+  )
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt
index ebf80db79..b8510fa92 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/StringSerializerTest.kt
@@ -16,42 +16,22 @@
  * 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.serializers.primitive
 
-import de.kuschku.libquassel.protocol.testutil.byteBufferOf
-import de.kuschku.libquassel.protocol.testutil.deserialize
-import de.kuschku.libquassel.protocol.variant.QtType
-import de.kuschku.libquassel.protocol.variant.qVariant
+import de.kuschku.libquassel.protocol.testutil.*
 import de.kuschku.libquassel.protocol.testutil.matchers.BomMatcherString
 import de.kuschku.libquassel.protocol.testutil.matchers.ByteBufferMatcher
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerDirect
-import de.kuschku.libquassel.protocol.testutil.testQtSerializerVariant
-import org.junit.Assert
-import org.junit.Test
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
 
 class StringSerializerTest {
-  @Test
-  fun testRoundtripStringList() {
-    val data = listOf("FeatureList")
-    testQtSerializerDirect(QStringListSerializer, data)
-    testQtSerializerVariant(QStringListSerializer, data)
-  }
-
-  @Test
-  fun testRoundtripVariantList() {
-    val data = listOf(qVariant("FeatureList", QtType.QString))
-    testQtSerializerDirect(QVariantListSerializer, data)
-    testQtSerializerVariant(QVariantListSerializer, data)
-  }
-
   @Test
   fun testBigListOfNaughtyStrings() {
     this::class.java.getResourceAsStream("/blns.txt")!!.bufferedReader(Charsets.UTF_8).forEachLine {
       // Ignore comments
       if (!it.startsWith('#')) {
-        testQtSerializerDirect(StringSerializerUtf8, it, BomMatcherString(it))
-        testQtSerializerDirect(StringSerializerUtf16, it, BomMatcherString(it))
+        testQtSerializerDirect(StringSerializerUtf8, it, matcher = BomMatcherString(it))
+        testQtSerializerDirect(StringSerializerUtf16, it, matcher = BomMatcherString(it))
       }
     }
   }
@@ -69,21 +49,21 @@ class StringSerializerTest {
     testQtSerializerDirect(StringSerializerAscii, data)
 
     val bufferAscii = StringSerializerAscii.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii))
-    testQtSerializerVariant(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii))
+    testQtSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    testQtSerializerVariant(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
 
     testQtSerializerDirect(StringSerializerUtf8, data)
 
     val bufferUtf8 = StringSerializerUtf8.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferUtf8, ByteBufferMatcher(bufferUtf8))
-    testQtSerializerVariant(ByteBufferSerializer, bufferUtf8, ByteBufferMatcher(bufferUtf8))
+    testQtSerializerDirect(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
+    testQtSerializerVariant(ByteBufferSerializer, bufferUtf8, matcher = ByteBufferMatcher(bufferUtf8))
 
     testQtSerializerDirect(StringSerializerUtf16, data)
     testQtSerializerVariant(StringSerializerUtf16, data)
 
     val bufferUtf16 = StringSerializerUtf16.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferUtf16, ByteBufferMatcher(bufferUtf16))
-    testQtSerializerVariant(ByteBufferSerializer, bufferUtf16, ByteBufferMatcher(bufferUtf16))
+    testQtSerializerDirect(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
+    testQtSerializerVariant(ByteBufferSerializer, bufferUtf16, matcher = ByteBufferMatcher(bufferUtf16))
   }
 
   @Test
@@ -96,23 +76,22 @@ class StringSerializerTest {
       ZO RELAXEN UND WATSCHEN DER BLINKENLICHTEN.
     """.trimIndent()
 
-    // @formatter:off
     val utf8Buffer = byteBufferOf(0, 0, 1, -118, 58, 32, 65, 67, 72, 84, 85, 78, 71, 33, 10, 65, 76, 76, 69, 83, 32, 84, 85, 82, 73, 83, 84, 69, 78, 32, 85, 78, 68, 32, 78, 79, 78, 84, 69, 75, 78, 73, 83, 67, 72, 69, 78, 32, 76, 79, 79, 75, 69, 78, 80, 69, 69, 80, 69, 82, 83, 33, 10, 68, 65, 83, 32, 75, 79, 77, 80, 85, 84, 69, 82, 77, 65, 83, 67, 72, 73, 78, 69, 32, 73, 83, 84, 32, 78, 73, 67, 72, 84, 32, 70, -61, -100, 82, 32, 68, 69, 82, 32, 71, 69, 70, 73, 78, 71, 69, 82, 80, 79, 75, 69, 78, 32, 85, 78, 68, 32, 77, 73, 84, 84, 69, 78, 71, 82, 65, 66, 69, 78, 33, 32, 79, 68, 69, 82, 87, 73, 83, 69, 32, 73, 83, 84, 32, 69, 65, 83, 89, 32, 84, 79, 32, 83, 67, 72, 78, 65, 80, 80, 69, 78, 32, 68, 69, 82, 32, 83, 80, 82, 73, 78, 71, 69, 78, 87, 69, 82, 75, 44, 32, 66, 76, 79, 87, 69, 78, 70, 85, 83, 69, 78, 32, 85, 78, 68, 32, 80, 79, 80, 80, 69, 78, 67, 79, 82, 75, 69, 78, 32, 77, 73, 84, 32, 83, 80, 73, 84, 90, 69, 78, 83, 80, 65, 82, 75, 69, 78, 46, 10, 73, 83, 84, 32, 78, 73, 67, 72, 84, 32, 70, -61, -100, 82, 32, 71, 69, 87, 69, 82, 75, 69, 78, 32, 66, 69, 73, 32, 68, 85, 77, 77, 75, 79, 80, 70, 69, 78, 46, 32, 68, 69, 82, 32, 82, 85, 66, 66, 69, 82, 78, 69, 67, 75, 69, 78, 32, 83, 73, 71, 72, 84, 83, 69, 69, 82, 69, 78, 32, 75, 69, 69, 80, 69, 78, 32, 68, 65, 83, 32, 67, 79, 84, 84, 79, 78, 80, 73, 67, 75, 69, 78, 32, 72, -61, -124, 78, 68, 69, 82, 32, 73, 78, 32, 68, 65, 83, 32, 80, 79, 67, 75, 69, 84, 83, 32, 77, 85, 83, 83, 46, 10, 90, 79, 32, 82, 69, 76, 65, 88, 69, 78, 32, 85, 78, 68, 32, 87, 65, 84, 83, 67, 72, 69, 78, 32, 68, 69, 82, 32, 66, 76, 73, 78, 75, 69, 78, 76, 73, 67, 72, 84, 69, 78, 46)
     val utf16Buffer = byteBufferOf(0, 0, 3, 14, 0, 58, 0, 32, 0, 65, 0, 67, 0, 72, 0, 84, 0, 85, 0, 78, 0, 71, 0, 33, 0, 10, 0, 65, 0, 76, 0, 76, 0, 69, 0, 83, 0, 32, 0, 84, 0, 85, 0, 82, 0, 73, 0, 83, 0, 84, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 78, 0, 79, 0, 78, 0, 84, 0, 69, 0, 75, 0, 78, 0, 73, 0, 83, 0, 67, 0, 72, 0, 69, 0, 78, 0, 32, 0, 76, 0, 79, 0, 79, 0, 75, 0, 69, 0, 78, 0, 80, 0, 69, 0, 69, 0, 80, 0, 69, 0, 82, 0, 83, 0, 33, 0, 10, 0, 68, 0, 65, 0, 83, 0, 32, 0, 75, 0, 79, 0, 77, 0, 80, 0, 85, 0, 84, 0, 69, 0, 82, 0, 77, 0, 65, 0, 83, 0, 67, 0, 72, 0, 73, 0, 78, 0, 69, 0, 32, 0, 73, 0, 83, 0, 84, 0, 32, 0, 78, 0, 73, 0, 67, 0, 72, 0, 84, 0, 32, 0, 70, 0, -36, 0, 82, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 71, 0, 69, 0, 70, 0, 73, 0, 78, 0, 71, 0, 69, 0, 82, 0, 80, 0, 79, 0, 75, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 77, 0, 73, 0, 84, 0, 84, 0, 69, 0, 78, 0, 71, 0, 82, 0, 65, 0, 66, 0, 69, 0, 78, 0, 33, 0, 32, 0, 79, 0, 68, 0, 69, 0, 82, 0, 87, 0, 73, 0, 83, 0, 69, 0, 32, 0, 73, 0, 83, 0, 84, 0, 32, 0, 69, 0, 65, 0, 83, 0, 89, 0, 32, 0, 84, 0, 79, 0, 32, 0, 83, 0, 67, 0, 72, 0, 78, 0, 65, 0, 80, 0, 80, 0, 69, 0, 78, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 83, 0, 80, 0, 82, 0, 73, 0, 78, 0, 71, 0, 69, 0, 78, 0, 87, 0, 69, 0, 82, 0, 75, 0, 44, 0, 32, 0, 66, 0, 76, 0, 79, 0, 87, 0, 69, 0, 78, 0, 70, 0, 85, 0, 83, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 80, 0, 79, 0, 80, 0, 80, 0, 69, 0, 78, 0, 67, 0, 79, 0, 82, 0, 75, 0, 69, 0, 78, 0, 32, 0, 77, 0, 73, 0, 84, 0, 32, 0, 83, 0, 80, 0, 73, 0, 84, 0, 90, 0, 69, 0, 78, 0, 83, 0, 80, 0, 65, 0, 82, 0, 75, 0, 69, 0, 78, 0, 46, 0, 10, 0, 73, 0, 83, 0, 84, 0, 32, 0, 78, 0, 73, 0, 67, 0, 72, 0, 84, 0, 32, 0, 70, 0, -36, 0, 82, 0, 32, 0, 71, 0, 69, 0, 87, 0, 69, 0, 82, 0, 75, 0, 69, 0, 78, 0, 32, 0, 66, 0, 69, 0, 73, 0, 32, 0, 68, 0, 85, 0, 77, 0, 77, 0, 75, 0, 79, 0, 80, 0, 70, 0, 69, 0, 78, 0, 46, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 82, 0, 85, 0, 66, 0, 66, 0, 69, 0, 82, 0, 78, 0, 69, 0, 67, 0, 75, 0, 69, 0, 78, 0, 32, 0, 83, 0, 73, 0, 71, 0, 72, 0, 84, 0, 83, 0, 69, 0, 69, 0, 82, 0, 69, 0, 78, 0, 32, 0, 75, 0, 69, 0, 69, 0, 80, 0, 69, 0, 78, 0, 32, 0, 68, 0, 65, 0, 83, 0, 32, 0, 67, 0, 79, 0, 84, 0, 84, 0, 79, 0, 78, 0, 80, 0, 73, 0, 67, 0, 75, 0, 69, 0, 78, 0, 32, 0, 72, 0, -60, 0, 78, 0, 68, 0, 69, 0, 82, 0, 32, 0, 73, 0, 78, 0, 32, 0, 68, 0, 65, 0, 83, 0, 32, 0, 80, 0, 79, 0, 67, 0, 75, 0, 69, 0, 84, 0, 83, 0, 32, 0, 77, 0, 85, 0, 83, 0, 83, 0, 46, 0, 10, 0, 90, 0, 79, 0, 32, 0, 82, 0, 69, 0, 76, 0, 65, 0, 88, 0, 69, 0, 78, 0, 32, 0, 85, 0, 78, 0, 68, 0, 32, 0, 87, 0, 65, 0, 84, 0, 83, 0, 67, 0, 72, 0, 69, 0, 78, 0, 32, 0, 68, 0, 69, 0, 82, 0, 32, 0, 66, 0, 76, 0, 73, 0, 78, 0, 75, 0, 69, 0, 78, 0, 76, 0, 73, 0, 67, 0, 72, 0, 84, 0, 69, 0, 78, 0, 46)
-    // @formatter:on
-    Assert.assertEquals(value, deserialize(StringSerializerUtf8, utf8Buffer))
-    Assert.assertEquals(value, deserialize(StringSerializerUtf16, utf16Buffer))
+
+    assertEquals(value, deserialize(StringSerializerUtf8, utf8Buffer))
+    assertEquals(value, deserialize(StringSerializerUtf16, utf16Buffer))
   }
 
   @Test
   fun testAscii() {
     // The simple solution: Just test all
-    val data = String(CharArray(256, Int::toChar).toList().shuffled().toCharArray())
+    val data = String((0x00.toChar()..0xFF.toChar()).toList().shuffled().toCharArray())
     testQtSerializerDirect(StringSerializerAscii, data)
 
     val bufferAscii = StringSerializerAscii.serializeRaw(data)
-    testQtSerializerDirect(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii))
-    testQtSerializerVariant(ByteBufferSerializer, bufferAscii, ByteBufferMatcher(bufferAscii))
+    testQtSerializerDirect(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    testQtSerializerVariant(ByteBufferSerializer, bufferAscii, matcher = ByteBufferMatcher(bufferAscii))
+    assertEquals(data, StringSerializerAscii.deserializeRaw(bufferAscii.rewind()))
   }
 }
-
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializerTest.kt
new file mode 100644
index 000000000..23e98606d
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UByteSerializerTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+import kotlin.experimental.inv
+
+class UByteSerializerTest {
+  @Test
+  fun testZero() = qtSerializerTest(
+    UByteSerializer,
+    0.toUByte(),
+    byteBufferOf(0)
+  )
+
+  @Test
+  fun testMinimal() = qtSerializerTest(
+    UByteSerializer,
+    UByte.MIN_VALUE,
+    byteBufferOf(0)
+  )
+
+  @Test
+  fun testMaximal() = qtSerializerTest(
+    UByteSerializer,
+    UByte.MAX_VALUE,
+    byteBufferOf(255u)
+  )
+
+  @Test
+  fun testAllOnes() = qtSerializerTest(
+    UByteSerializer,
+    0.toUByte().inv(),
+    byteBufferOf(255u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializerTest.kt
new file mode 100644
index 000000000..09edb18aa
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UIntSerializerTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+import kotlin.experimental.inv
+
+class UIntSerializerTest {
+  @Test
+  fun testZero() = qtSerializerTest(
+    UIntSerializer,
+    0.toUInt(),
+    byteBufferOf(0, 0, 0, 0)
+  )
+
+  @Test
+  fun testMinimal() = qtSerializerTest(
+    UIntSerializer,
+    UInt.MIN_VALUE,
+    byteBufferOf(0, 0, 0, 0)
+  )
+
+  @Test
+  fun testMaximal() = qtSerializerTest(
+    UIntSerializer,
+    UInt.MAX_VALUE,
+    byteBufferOf(255u, 255u, 255u, 255u)
+  )
+
+  @Test
+  fun testAllOnes() = qtSerializerTest(
+    UIntSerializer,
+    0.toUInt().inv(),
+    byteBufferOf(255u, 255u, 255u, 255u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializerTest.kt
new file mode 100644
index 000000000..3a68c7c63
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/ULongSerializerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+
+class ULongSerializerTest {
+  @Test
+  fun testZero() = qtSerializerTest(
+    ULongSerializer,
+    0.toULong(),
+    byteBufferOf(0, 0, 0, 0, 0, 0, 0, 0)
+  )
+
+  @Test
+  fun testMinimal() = qtSerializerTest(
+    ULongSerializer,
+    ULong.MIN_VALUE,
+    byteBufferOf(0, 0, 0, 0, 0, 0, 0, 0)
+  )
+
+  @Test
+  fun testMaximal() = qtSerializerTest(
+    ULongSerializer,
+    ULong.MAX_VALUE,
+    byteBufferOf(255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u)
+  )
+
+  @Test
+  fun testAllOnes() = qtSerializerTest(
+    ULongSerializer,
+    0.toULong().inv(),
+    byteBufferOf(255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializerTest.kt
new file mode 100644
index 000000000..29863bbca
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/serializers/primitive/UShortSerializerTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2020 Janne Mareike Koschinski
+ * Copyright (c) 2020 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.serializers.primitive
+
+import de.kuschku.libquassel.protocol.testutil.byteBufferOf
+import de.kuschku.libquassel.protocol.testutil.qtSerializerTest
+import org.junit.jupiter.api.Test
+import kotlin.experimental.inv
+
+class UShortSerializerTest {
+  @Test
+  fun testZero() = qtSerializerTest(
+    UShortSerializer,
+    0.toUShort(),
+    byteBufferOf(0, 0)
+  )
+
+  @Test
+  fun testMinimal() = qtSerializerTest(
+    UShortSerializer,
+    UShort.MIN_VALUE,
+    byteBufferOf(0, 0)
+  )
+
+  @Test
+  fun testMaximal() = qtSerializerTest(
+    UShortSerializer,
+    UShort.MAX_VALUE,
+    byteBufferOf(255u, 255u)
+  )
+
+  @Test
+  fun testAllOnes() = qtSerializerTest(
+    UShortSerializer,
+    0.toUShort().inv(),
+    byteBufferOf(255u, 255u)
+  )
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt
index 2d415ea2f..74efbd603 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/byteBufferOf.kt
@@ -16,10 +16,7 @@
  * 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.testutil
-
 import java.nio.ByteBuffer
-
 inline fun byteBufferOf(vararg elements: Byte) = ByteBuffer.wrap(byteArrayOf(*elements))
 inline fun byteBufferOf(vararg elements: UByte) = ByteBuffer.wrap(ubyteArrayOf(*elements).toByteArray())
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt
index 1ae7d9908..1405e746a 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/deserialize.kt
@@ -16,46 +16,59 @@
  * 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.testutil
-
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeMapSerializer
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer
 import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
 import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert
-import org.junit.Assert
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
 import java.nio.ByteBuffer
-
-fun <T> deserialize(serializer: QtSerializer<T>, buffer: ByteBuffer): T {
-  val connectionFeatureSet = FeatureSet.build()
-  val result = serializer.deserialize(
-    buffer,
-    connectionFeatureSet
-  )
-  Assert.assertEquals(0, buffer.remaining())
+fun <T> deserialize(
+  serializer: QtSerializer<T>,
+  buffer: ByteBuffer,
+  featureSet: FeatureSet = FeatureSet.all()
+): T {
+  val result = serializer.deserialize(buffer, featureSet)
+  assertEquals(0, buffer.remaining())
   return result
 }
-
-fun <T> testDeserialize(serializer: QtSerializer<T>, matcher: Matcher<in T>, buffer: ByteBuffer) {
-  val after = deserialize(serializer, buffer)
-  MatcherAssert.assertThat(after, matcher)
+fun <T> testDeserialize(
+  serializer: QtSerializer<T>,
+  matcher: Matcher<in T>,
+  buffer: ByteBuffer,
+  featureSet: FeatureSet = FeatureSet.all()
+) {
+  val after = deserialize(serializer, buffer, featureSet)
+  assertThat(after, matcher)
 }
-
-fun <T> testDeserialize(serializer: QtSerializer<T>, data: T, buffer: ByteBuffer) {
-  val after = deserialize(serializer, buffer)
-  Assert.assertEquals(data, after)
+fun <T> testDeserialize(
+  serializer: QtSerializer<T>,
+  data: T,
+  buffer: ByteBuffer,
+  featureSet: FeatureSet = FeatureSet.all()
+) {
+  val after = deserialize(serializer, buffer, featureSet)
+  assertEquals(data, after)
 }
-
-fun <T> testDeserialize(serializer: HandshakeSerializer<T>, matcher: Matcher<in T>, buffer: ByteBuffer) {
-  val map = deserialize(HandshakeMapSerializer, buffer)
+fun <T> testDeserialize(
+  serializer: HandshakeSerializer<T>,
+  matcher: Matcher<in T>,
+  buffer: ByteBuffer,
+  featureSet: FeatureSet = FeatureSet.all()
+) {
+  val map = deserialize(HandshakeMapSerializer, buffer, featureSet)
   val after = serializer.deserialize(map)
-  MatcherAssert.assertThat(after, matcher)
+  assertThat(after, matcher)
 }
-
-fun <T> testDeserialize(serializer: HandshakeSerializer<T>, data: T, buffer: ByteBuffer) {
-  val map = deserialize(HandshakeMapSerializer, buffer)
+fun <T> testDeserialize(
+  serializer: HandshakeSerializer<T>,
+  data: T,
+  buffer: ByteBuffer,
+  featureSet: FeatureSet = FeatureSet.all()
+) {
+  val map = deserialize(HandshakeMapSerializer, buffer, featureSet)
   val after = serializer.deserialize(map)
-  Assert.assertEquals(data, after)
+  assertEquals(data, after)
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/handshakeSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/handshakeSerializerTest.kt
new file mode 100644
index 000000000..65941fda6
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/handshakeSerializerTest.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.testutil
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer
+import org.hamcrest.Matcher
+import java.nio.ByteBuffer
+
+fun <T> handshakeSerializerTest(
+  serializer: HandshakeSerializer<T>,
+  value: T,
+  encoded: ByteBuffer? = null,
+  matcher: ((T) -> Matcher<T>)? = null,
+  featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
+  deserializeFeatureSet: FeatureSet = FeatureSet.all(),
+) {
+  for (featureSet in featureSets) {
+    testHandshakeSerializerDirect(serializer, value)
+    testHandshakeSerializerEncoded(serializer, value, featureSet)
+  }
+  if (encoded != null) {
+    if (matcher != null) {
+      testDeserialize(serializer, matcher(value), encoded.rewind(), deserializeFeatureSet)
+    } else {
+      testDeserialize(serializer, value, encoded.rewind(), deserializeFeatureSet)
+    }
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt
index bd00fb1ed..511c69c99 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherChar.kt
@@ -16,7 +16,6 @@
  * 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.testutil.matchers
 
 import org.hamcrest.BaseMatcher
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt
index de2e83e16..1ca92f1f0 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/BomMatcherString.kt
@@ -16,21 +16,16 @@
  * 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.testutil.matchers
-
 import org.hamcrest.BaseMatcher
 import org.hamcrest.Description
-
-class BomMatcherString(private val expected: String) : BaseMatcher<String>() {
+class BomMatcherString(private val expected: String?) : BaseMatcher<String?>() {
   private val malformed = charArrayOf(
     '￾', ''
   )
-
   override fun describeTo(description: Description?) {
     description?.appendText(expected)
   }
-
   override fun matches(item: Any?) =
-    (item as? String)?.endsWith(expected.trimStart(*malformed)) == true
+    (item as? String)?.endsWith(expected?.trimStart(*malformed) ?: "") == true
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt
index 98a904e32..e58c9aaae 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/ByteBufferMatcher.kt
@@ -16,19 +16,39 @@
  * 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.testutil.matchers
-
 import de.kuschku.libquassel.protocol.io.contentToString
 import org.hamcrest.BaseMatcher
 import org.hamcrest.Description
 import java.nio.ByteBuffer
 
-class ByteBufferMatcher(private val expected: ByteBuffer) : BaseMatcher<ByteBuffer>() {
+class ByteBufferMatcher(buffer: ByteBuffer?) : BaseMatcher<ByteBuffer>() {
+  private val expected = buffer?.let { original ->
+    val copy = ByteBuffer.allocateDirect(original.limit())
+    original.rewind()
+    copy.put(original)
+    copy.rewind()
+    original.rewind()
+    copy
+  }
+
   override fun describeTo(description: Description?) {
-    description?.appendText(expected.contentToString())
+    description?.appendText(expected?.contentToString())
   }
 
-  override fun matches(item: Any?) =
-    (item as? ByteBuffer)?.clear()?.contentToString() == expected.clear().contentToString()
+  override fun describeMismatch(item: Any?, description: Description?) {
+    description?.appendText("was ")
+    description?.appendText((item as? ByteBuffer)?.rewind()?.contentToString())
+  }
+
+  override fun matches(item: Any?): Boolean {
+    return if (item is ByteBuffer && expected is ByteBuffer) {
+      val expected = expected.rewind().contentToString()
+      val actual = item.rewind().contentToString()
+
+      expected == actual
+    } else {
+      false
+    }
+  }
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/TemporalMatcher.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/TemporalMatcher.kt
new file mode 100644
index 000000000..848e016e9
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/matchers/TemporalMatcher.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.testutil.matchers
+
+import org.hamcrest.BaseMatcher
+import org.hamcrest.Description
+import org.threeten.bp.Instant
+import org.threeten.bp.LocalDateTime
+import org.threeten.bp.OffsetDateTime
+import org.threeten.bp.ZonedDateTime
+import org.threeten.bp.temporal.Temporal
+
+class TemporalMatcher(
+  private val expected: Temporal
+) : BaseMatcher<Temporal>() {
+  override fun describeTo(description: Description?) {
+    description?.appendText(expected.toString())
+  }
+
+  override fun matches(item: Any?): Boolean {
+    return when {
+      expected is ZonedDateTime && item is ZonedDateTime ->
+        expected == item
+      expected is ZonedDateTime && item is OffsetDateTime ->
+        expected.toOffsetDateTime() == item
+      expected is OffsetDateTime && item is OffsetDateTime ->
+        expected == item
+      expected is LocalDateTime && item is LocalDateTime ->
+        expected == item
+      expected is Instant && item is Instant ->
+        expected == item
+      else ->
+        false
+    }
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/qtSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/qtSerializerTest.kt
new file mode 100644
index 000000000..278da43f8
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/qtSerializerTest.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.testutil
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
+import de.kuschku.libquassel.protocol.serializers.primitive.QuasselSerializer
+import org.hamcrest.Matcher
+import java.nio.ByteBuffer
+fun <T> qtSerializerTest(
+  serializer: QtSerializer<T>,
+  value: T,
+  encoded: ByteBuffer? = null,
+  matcher: ((T) -> Matcher<T>)? = null,
+  featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
+  deserializeFeatureSet: FeatureSet = FeatureSet.all(),
+) {
+  for (featureSet in featureSets) {
+    testQtSerializerDirect(serializer, value, featureSet, matcher?.invoke(value))
+    testQtSerializerVariant(serializer, value, featureSet, matcher?.invoke(value))
+  }
+  if (encoded != null) {
+    if (matcher != null) {
+      testDeserialize(serializer, matcher(value), encoded.rewind(), deserializeFeatureSet)
+    } else {
+      testDeserialize(serializer, value, encoded.rewind(), deserializeFeatureSet)
+    }
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/quasselSerializerTest.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/quasselSerializerTest.kt
new file mode 100644
index 000000000..1d3654472
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/quasselSerializerTest.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.testutil
+
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.serializers.primitive.QuasselSerializer
+import org.hamcrest.Matcher
+import java.nio.ByteBuffer
+
+fun <T> quasselSerializerTest(
+  serializer: QuasselSerializer<T>,
+  value: T,
+  encoded: ByteBuffer? = null,
+  matcher: ((T) -> Matcher<T>)? = null,
+  featureSets: List<FeatureSet> = listOf(FeatureSet.none(), FeatureSet.all()),
+  deserializeFeatureSet: FeatureSet = FeatureSet.all(),
+) {
+  for (featureSet in featureSets) {
+    testQuasselSerializerDirect(serializer, value, featureSet, matcher?.invoke(value))
+    testQuasselSerializerVariant(serializer, value, featureSet, matcher?.invoke(value))
+  }
+  if (encoded != null) {
+    if (matcher != null) {
+      testDeserialize(serializer, matcher(value), encoded.rewind(), deserializeFeatureSet)
+    } else {
+      testDeserialize(serializer, value, encoded.rewind(), deserializeFeatureSet)
+    }
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt
index a4c23c3f8..45208f297 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerDirect.kt
@@ -16,25 +16,22 @@
  * 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.testutil
 
-import de.kuschku.libquassel.protocol.features.FeatureSet
-import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
-import de.kuschku.libquassel.protocol.io.print
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer
-import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
 import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert
-import org.junit.Assert
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testHandshakeSerializerDirect(serializer: HandshakeSerializer<T>, data: T, matcher: Matcher<T>? = null) {
+fun <T> testHandshakeSerializerDirect(
+  serializer: HandshakeSerializer<T>,
+  data: T,
+  matcher: Matcher<T>? = null
+) {
   val after = serializer.deserialize(serializer.serialize(data))
-
   if (matcher != null) {
-    MatcherAssert.assertThat(data, matcher)
+    assertThat(after, matcher)
   } else {
-    Assert.assertEquals(data, after)
+    assertEquals(data, after)
   }
 }
-
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt
index 1ace04a4f..9bdc85ac1 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testHandshakeSerializerEncoded.kt
@@ -16,34 +16,31 @@
  * 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.testutil
-
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
 import de.kuschku.libquassel.protocol.io.print
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeMapSerializer
 import de.kuschku.libquassel.protocol.serializers.handshake.HandshakeSerializer
-import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
 import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert
-import org.junit.Assert
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testHandshakeSerializerEncoded(serializer: HandshakeSerializer<T>, data: T, matcher: Matcher<T>? = null) {
-  val connectionFeatureSet = FeatureSet.build()
+fun <T> testHandshakeSerializerEncoded(
+  serializer: HandshakeSerializer<T>,
+  data: T,
+  featureSet: FeatureSet = FeatureSet.all(),
+  matcher: Matcher<T>? = null
+) {
   val buffer = ChainedByteBuffer()
-
-  HandshakeMapSerializer.serialize(buffer, serializer.serialize(data), connectionFeatureSet)
+  HandshakeMapSerializer.serialize(buffer, serializer.serialize(data), featureSet)
   val result = buffer.toBuffer()
   result.print()
-  val after = serializer.deserialize(HandshakeMapSerializer.deserialize(result, connectionFeatureSet))
-
-  Assert.assertEquals(0, result.remaining())
-
+  val after = serializer.deserialize(HandshakeMapSerializer.deserialize(result, featureSet))
+  assertEquals(0, result.remaining())
   if (matcher != null) {
-    MatcherAssert.assertThat(data, matcher)
+    assertThat(after, matcher)
   } else {
-    Assert.assertEquals(data, after)
+    assertEquals(data, after)
   }
 }
-
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt
index 1855cab9e..efa675b76 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerDirect.kt
@@ -16,31 +16,30 @@
  * 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.testutil
-
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
 import de.kuschku.libquassel.protocol.io.print
 import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
 import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert
-import org.junit.Assert
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testQtSerializerDirect(serializer: QtSerializer<T>, data: T, matcher: Matcher<T>? = null) {
-  val connectionFeatureSet = FeatureSet.build()
+fun <T> testQtSerializerDirect(
+  serializer: QtSerializer<T>,
+  data: T,
+  featureSet: FeatureSet = FeatureSet.all(),
+  matcher: Matcher<T>? = null
+) {
   val buffer = ChainedByteBuffer()
-
-  serializer.serialize(buffer, data, connectionFeatureSet)
+  serializer.serialize(buffer, data, featureSet)
   val result = buffer.toBuffer()
   result.print()
-  val after = serializer.deserialize(result, connectionFeatureSet)
-
-  Assert.assertEquals(0, result.remaining())
+  val after = serializer.deserialize(result, featureSet)
+  assertEquals(0, result.remaining())
   if (matcher != null) {
-    MatcherAssert.assertThat(data, matcher)
+    assertThat(after, matcher)
   } else {
-    Assert.assertEquals(data, after)
+    assertEquals(data, after)
   }
 }
-
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt
index e1da9981e..6164467b1 100644
--- a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQtSerializerVariant.kt
@@ -16,9 +16,7 @@
  * 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.testutil
-
 import de.kuschku.libquassel.protocol.features.FeatureSet
 import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
 import de.kuschku.libquassel.protocol.io.print
@@ -26,22 +24,25 @@ import de.kuschku.libquassel.protocol.serializers.primitive.QVariantSerializer
 import de.kuschku.libquassel.protocol.serializers.primitive.QtSerializer
 import de.kuschku.libquassel.protocol.variant.QVariant
 import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert
-import org.junit.Assert
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
 
-fun <T> testQtSerializerVariant(serializer: QtSerializer<T>, data: T, matcher: Matcher<in T>? = null) {
-  val connectionFeatureSet = FeatureSet.build()
+fun <T> testQtSerializerVariant(
+  serializer: QtSerializer<T>,
+  data: T,
+  featureSet: FeatureSet = FeatureSet.all(),
+  matcher: Matcher<in T>? = null
+) {
   val buffer = ChainedByteBuffer()
-
-  QVariantSerializer.serialize(buffer, QVariant.of(data, serializer), connectionFeatureSet)
+  QVariantSerializer.serialize(buffer, QVariant.of(data, serializer), featureSet)
   val result = buffer.toBuffer()
   result.print()
-  val after = QVariantSerializer.deserialize(result, connectionFeatureSet)
-
-  Assert.assertEquals(0, result.remaining())
+  val after = QVariantSerializer.deserialize(result, featureSet)
+  assertEquals(0, result.remaining())
   if (matcher != null) {
-    MatcherAssert.assertThat(data, matcher)
+    @Suppress("UNCHECKED_CAST")
+    assertThat(after.value() as T, matcher)
   } else {
-    Assert.assertEquals(data, after.value())
+    assertEquals(data, after.value())
   }
 }
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerDirect.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerDirect.kt
new file mode 100644
index 000000000..2a23c84fd
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerDirect.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.testutil
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.io.print
+import de.kuschku.libquassel.protocol.serializers.primitive.QuasselSerializer
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
+
+fun <T> testQuasselSerializerDirect(
+  serializer: QuasselSerializer<T>,
+  data: T,
+  featureSet: FeatureSet = FeatureSet.all(),
+  matcher: Matcher<T>? = null
+) {
+  val buffer = ChainedByteBuffer()
+  serializer.serialize(buffer, data, featureSet)
+  val result = buffer.toBuffer()
+  println("direct")
+  result.print()
+  val after = serializer.deserialize(result, featureSet)
+  assertEquals(0, result.remaining())
+  if (matcher != null) {
+    assertThat(after, matcher)
+  } else {
+    assertEquals(data, after)
+  }
+}
diff --git a/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerVariant.kt b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerVariant.kt
new file mode 100644
index 000000000..6c83a3fec
--- /dev/null
+++ b/protocol/src/test/kotlin/de/kuschku/libquassel/protocol/testutil/testQuasselSerializerVariant.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.testutil
+import de.kuschku.libquassel.protocol.features.FeatureSet
+import de.kuschku.libquassel.protocol.io.ChainedByteBuffer
+import de.kuschku.libquassel.protocol.io.print
+import de.kuschku.libquassel.protocol.serializers.primitive.QVariantSerializer
+import de.kuschku.libquassel.protocol.serializers.primitive.QuasselSerializer
+import de.kuschku.libquassel.protocol.variant.QVariant
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.jupiter.api.Assertions.assertEquals
+
+fun <T> testQuasselSerializerVariant(
+  serializer: QuasselSerializer<T>,
+  data: T,
+  featureSet: FeatureSet = FeatureSet.all(),
+  matcher: Matcher<in T>? = null
+) {
+  val buffer = ChainedByteBuffer()
+  QVariantSerializer.serialize(buffer, QVariant.of(data, serializer), featureSet)
+  val result = buffer.toBuffer()
+  result.print()
+  val after = QVariantSerializer.deserialize(result, featureSet)
+  assertEquals(0, result.remaining())
+  if (matcher != null) {
+    @Suppress("UNCHECKED_CAST")
+    assertThat(after.value() as T, matcher)
+  } else {
+    assertEquals(data, after.value())
+  }
+}
-- 
GitLab