diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e719474e393ae5d21373c1e56e53ec40e2e35751..9a7edc30d4ec569a91d129c6055803a3aa90ab7e 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 8c7ddc04eea5321401c7cf583111db6c056f0bf9..393ac2018b3b0aa91362e120487c84311b5e1dd1 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 0000000000000000000000000000000000000000..cc6a8daa3bd7f9e9e584d8e863668cfe3fda28b0
--- /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 eb000188f23372a6e1019a931c316060e4bfb0e3..da9953d99a0a5676e16a17658a9b949a20013ad7 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 0000000000000000000000000000000000000000..7398893ff40d2c344979595960e87a64a3aae3c8
--- /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 e29c2b7cf8adba258def78f7e25ed9fc09d59e61..23ca9dcc2d01fae398de31a6426d85f25aa31386 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 8018a4444c8187666b91e3fac1074fffdebccc02..1eb55bb51895779f0d4fd2be1d31daecc1dd0495 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 0000000000000000000000000000000000000000..3485bf8a268c5a4d5f18e287077874f8a623ea59
--- /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 5368ece0a654a7c436a3a657ea83f2d760179a1e..c3ad86bf6dfcf979fab67553e477b6bf83433dae 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 2bfaa2a2f1cca5ec002dc80f271d6819946bc829..27124f8ee225aafee1a81bc432af32d0b5e0d42d 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 3173028f856176bfc5af805b1448c5b55289f93f..c8502a6f7422901c47f30b9e311791166062114e 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 eefd1480561b6469eba509c78069c9afe45b2347..a1585029db9dd569ac153139e1680551af6f264d 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 450b01061e787662394335374132551bf15251cd..a727ed77e12c934f80f4eeadb3547cc6cce08a41 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 0000000000000000000000000000000000000000..6217ea633f9de7f58ba1349a7d4766a41ec452a5
--- /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 0000000000000000000000000000000000000000..9cfaf16be64ad844982da5b41827769b8c063939
--- /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 ebc0427e2674628dca22900f21b4224af55d8746..4eaf85cad7a2d503f83d8586c531de6cb52cef3d 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 b3ec8e15e38524dc414689701c732189c18037e8..b1caf13ea633a11f41222d15bd2c45c098c02197 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 89b05757c129640f7568fcea4342be5f12267b15..a8001289c37e5d78772a2b6d302174ba7914a61f 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 0ad2b2cdd10b74d5dd27bd1622b7588e5ec14bf8..7d2dae33fa0a4751c00ac5b1f81a32789345d9be 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 65114cd0caab15fb7028aef5eb6e58801067f1a0..3cbefd468b4e910bb38975785913801ccad6b455 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 23c93e97607a95c36562dfa8b8c456c3f51abfde..4d05c3d019ee78e74cce711f018ad84c97f09f70 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 a93e623c9bb567bbdf2bff24606b8cbed56e039d..c608474f877e2dd829b720105163b5d72fc6f452 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 8bd2dbe2875c3d404e7e0dd379a4689cdec6130d..bbefa87b79f1f53dff6c0208f109f50a59cab76c 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 91e0726c25b3b5752efc14a2d8297f62f99b0200..a3c51646675452015705a0ff2ba6d69a59df910f 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 ae609475c8d1f192f47774848749edef61ec0369..44d0ca83018af758c69c17068df4c572f183be99 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 da916d3873595ead596f12c975d19fbec9b006c5..8d45bfed5b7166d1f0f96233bf45cbd6024504f0 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 a5afb2c50323b8f7072c8a5baab1dbff4165c3d0..e0882d5957e13039dcbdc2c8490692b229605827 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 6b6c01114dd5db1ccec6b487ab85af675148eebb..93cb9358afe94d4e153c0ca294839c0fb700d59d 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 3f9ef4037f5a0ab6366ef217046e3457915d0877..c0b682a405e4675332723e89ed0182616519500d 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 77e58168518ce292090a446881903565a0f46f8c..64f13fa4e5e9652056ec51db0858f247750aa89f 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 c03ae034c1194b3915d14bdf5e726aa0c6ee90f9..fca778612b04823adefb953971b46d76d7992b22 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 64f50e7aff4aa83435315d31fdb560c4014cb0d7..c6dfa9a3cabeefbbb630b45542ab0034ff995a0f 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 0000000000000000000000000000000000000000..f32ae8ae16b6cc5ca103be719b988c66234bed59
--- /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 fa706ec99120284fccccdf7b8e7cb9413e3cfc7f..e109746fcf6f7ec747cf5949e3ee88ae61e70e92 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 600952e1a53f3ccc329eb993afc58a86ca3a0468..acb9c99600fb482d198067428508aa68ac8dcec3 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 0000000000000000000000000000000000000000..1f2352a43341ae00f9d006da2a1a5341a19cd743
--- /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 0000000000000000000000000000000000000000..5b6c41938cca43669cda034af09884b69ad1a25f
--- /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 0000000000000000000000000000000000000000..ea12027a0640f92713fc34db20e026063c384fdf
--- /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 26161a3c65687f3d7b2b53508686b93d5cc3882e..019f3245ec3ea247997abd6aa5c5e36843e60411 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 48835fdc6f485ed029dd8b61dca41d28f0d95069..f6cc0729cd4dcda3587342c99725fcd718e4077d 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 0000000000000000000000000000000000000000..f07ad5f6c62eac08c495fc622741b765a95664cf
--- /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 8c6f8f88ec1da79c1a138fffeae70094355c9fc1..c95da6fd0e87b15ef356d099127cbac590e9bf24 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 0000000000000000000000000000000000000000..88f60db3fb84620165479476b32573d1c2524f2b
--- /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 ad6a534688e3c552e65c7019df1075fae49104b5..644a03b47310fb208b03a2c74bc1d9a00089c0b0 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 ebf80db7924d4168e2c77ea30a7188f1c1faccbd..b8510fa927337890f4e7d52f417afab4362c9e71 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 0000000000000000000000000000000000000000..23e98606d459b18770566112ef22adcb254558f8
--- /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 0000000000000000000000000000000000000000..09edb18aaabf7e1ccdd1a0747c7bf9b83a4b8a0b
--- /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 0000000000000000000000000000000000000000..3a68c7c63b74cbf90a0821f6486d49e3a14297cc
--- /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 0000000000000000000000000000000000000000..29863bbca06972625f3483100576a69979b81304
--- /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 2d415ea2f7e4803ae25671e4c7221e0b16be683b..74efbd6038bd4342144f33d15aae5c507e1a782c 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 1ae7d9908851f746e3bcf2b5e4675ae66bb36d6c..1405e746aeab775419088747841b8b4b3bbd27cd 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 0000000000000000000000000000000000000000..65941fda60bbd60c603efd1dd2ac0b3752f9430a
--- /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 bd00fb1edb99c09e0d751bd3fd3a12cf18ed4869..511c69c99dee94f6738f8169f6a26b4da5fc9919 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 de2e83e1639291ff8bcea8304bdf85389c90f75a..1ca92f1f0a3fd56473c3f674f75de4269659d53a 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 98a904e325c5553a26e7263ee9581b6e74632cc1..e58c9aaaee4a5ccbf7df9b06a1b0dbc5b13deecd 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 0000000000000000000000000000000000000000..848e016e9d8dbce4568c621e40df8dace3ea279b
--- /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 0000000000000000000000000000000000000000..278da43f82613fb359e83d8404d9e3a6665b7d04
--- /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 0000000000000000000000000000000000000000..1d3654472f352e05cfa3a91a7a62059992ac294c
--- /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 a4c23c3f874e0ccfeb122bace63f0a113fadff46..45208f297c460ddd1b9d9720fc0a907735c8d30f 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 1ace04a4f72b3cac3312280cdc9e0ca0804c1f1f..9bdc85ac141fa34293167b97ee363c114deeebc3 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 1855cab9e09d460060e62267204d9ed291e40c21..efa675b766421365cd710591c89b1d2d18ca6a2d 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 e1da9981e23b3d0adc96136b954ee675b1ad703a..6164467b13e80148de093a9f12335872a0327bdf 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 0000000000000000000000000000000000000000..2a23c84fdb897244986613705552cc98583da9fd
--- /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 0000000000000000000000000000000000000000..6c83a3fec886cf9f6e6c957b104ce5d4b8c24120
--- /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())
+  }
+}