From b1d601b272fe761af30a8f414f1038bc5327fcc7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20K=C3=BCrten?= <sebastian@mobanisto.de>
Date: Fri, 1 Oct 2021 21:16:06 +0200
Subject: [PATCH] Add deterministic test data defined using DSL

---
 .../briar/desktop/testdata/Conversations.kt   | 24 +++++
 .../desktop/testdata/ConversationsData.kt     | 55 ++++++++++++
 .../desktop/testdata/ConversationsDsl.kt      | 62 +++++++++++++
 .../testdata/DeterministicTestDataCreator.kt  |  2 +-
 .../DeterministicTestDataCreatorImpl.kt       | 88 +++++++++----------
 5 files changed, 184 insertions(+), 47 deletions(-)
 create mode 100644 src/test/kotlin/org/briarproject/briar/desktop/testdata/Conversations.kt
 create mode 100644 src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsData.kt
 create mode 100644 src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsDsl.kt

diff --git a/src/test/kotlin/org/briarproject/briar/desktop/testdata/Conversations.kt b/src/test/kotlin/org/briarproject/briar/desktop/testdata/Conversations.kt
new file mode 100644
index 0000000000..bbcf0d92b0
--- /dev/null
+++ b/src/test/kotlin/org/briarproject/briar/desktop/testdata/Conversations.kt
@@ -0,0 +1,24 @@
+package org.briarproject.briar.desktop.testdata
+
+import java.time.LocalDateTime
+
+data class Conversations(
+    val persons: List<Conversation>
+)
+
+data class Conversation(
+    val name: String,
+    var messages: List<Message>
+)
+
+data class Message(
+    val text: String,
+    val direction: Direction,
+    val date: LocalDateTime,
+    val read: Boolean
+)
+
+enum class Direction {
+    INCOMING,
+    OUTGOING
+}
diff --git a/src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsData.kt b/src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsData.kt
new file mode 100644
index 0000000000..38cdea48a5
--- /dev/null
+++ b/src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsData.kt
@@ -0,0 +1,55 @@
+package org.briarproject.briar.desktop.testdata
+
+import java.time.LocalDateTime.of as dt
+
+val conversations = conversations {
+    conversation {
+        contactName = "Bob"
+        outgoing {
+            text = "Hi, Bob"
+            read = true
+            date = "2020-12-24 20:10:15"
+        }
+        incoming {
+            text = "What's up?"
+            read = true
+            date = "2020-12-24 20:11:05"
+        }
+        outgoing {
+            text = "Nothing much, lately"
+            read = false
+            date = "2020-12-24 20:12:34"
+        }
+    }
+    conversation {
+        contactName = "Chuck"
+        val start = dt(2020, 1, 12, 19, 43, 17)
+        outgoing {
+            text = "Hey Chuck!"
+            read = true
+            date = start
+        }
+        incoming {
+            text = "Good evening, Alice"
+            read = true
+            date = start.plusSeconds(100)
+        }
+    }
+    conversation {
+        contactName = "Dan"
+        outgoing {
+            text = "Welcome to Briar!"
+            read = true
+            date = "2019-02-13 13:15:00"
+        }
+    }
+}
+
+fun main(args: Array<String>) {
+    for (conversation in conversations.persons) {
+        println("conversation with: ${conversation.name}")
+        for (message in conversation.messages) {
+            println("  ${message.direction} ${message.text} ${message.read} ${message.date}")
+        }
+    }
+}
diff --git a/src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsDsl.kt b/src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsDsl.kt
new file mode 100644
index 0000000000..3a815a70a6
--- /dev/null
+++ b/src/test/kotlin/org/briarproject/briar/desktop/testdata/ConversationsDsl.kt
@@ -0,0 +1,62 @@
+package org.briarproject.briar.desktop.testdata
+
+import org.briarproject.briar.desktop.testdata.Direction.INCOMING
+import org.briarproject.briar.desktop.testdata.Direction.OUTGOING
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+
+@DslMarker
+annotation class ConversationsDsl
+
+fun conversations(block: ConversationsBuilder.() -> Unit): Conversations = ConversationsBuilder().apply(block).build()
+
+@ConversationsDsl
+class ConversationsBuilder {
+
+    private val conversations = mutableListOf<Conversation>()
+
+    fun conversation(block: ConversationBuilder.() -> Unit) {
+        conversations.add(ConversationBuilder().apply(block).build())
+    }
+
+    fun build(): Conversations = Conversations(conversations)
+}
+
+@ConversationsDsl
+class ConversationBuilder {
+
+    var contactName: String = ""
+
+    private val messages = mutableListOf<Message>()
+
+    fun incoming(block: MessageBuilder.() -> Unit) {
+        messages.add(MessageBuilder(INCOMING).apply(block).build())
+    }
+
+    fun outgoing(block: MessageBuilder.() -> Unit) {
+        messages.add(MessageBuilder(OUTGOING).apply(block).build())
+    }
+
+    fun build(): Conversation = Conversation(contactName, messages)
+}
+
+var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+
+@ConversationsDsl
+class MessageBuilder(private val direction: Direction) {
+
+    var text: String = ""
+    var read: Boolean = false
+
+    private var sent: LocalDateTime = LocalDateTime.now()
+    var date: Any = ""
+        set(value) {
+            if (value is String) {
+                sent = LocalDateTime.parse(value, formatter)
+            } else if (value is LocalDateTime) {
+                sent = value
+            }
+        }
+
+    fun build(): Message = Message(text, direction, sent, read)
+}
diff --git a/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt b/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt
index af242ba5d3..e1670964cf 100644
--- a/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt
+++ b/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt
@@ -18,5 +18,5 @@ interface DeterministicTestDataCreator {
 
     @IoExecutor
     @Throws(DbException::class)
-    fun addContact(name: String?, alias: Boolean, avatar: Boolean): Contact?
+    fun addContact(name: String?, alias: String?, avatar: Boolean): Contact?
 }
diff --git a/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt b/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
index 72cdd1d450..5d6a340012 100644
--- a/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
+++ b/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
@@ -32,22 +32,25 @@ import org.briarproject.briar.api.avatar.AvatarMessageEncoder
 import org.briarproject.briar.api.messaging.MessagingManager
 import org.briarproject.briar.api.messaging.PrivateMessageFactory
 import org.briarproject.briar.api.test.TestAvatarCreator
-import org.briarproject.briar.test.TestData
 import java.io.IOException
 import java.io.InputStream
+import java.time.ZoneOffset
 import java.util.Random
 import java.util.UUID
 import java.util.concurrent.Executor
 import java.util.logging.Level
 import java.util.logging.Logger
 import javax.inject.Inject
+import kotlin.math.min
 
 class DeterministicTestDataCreatorImpl @Inject internal constructor(
-    private val authorFactory: AuthorFactory, private val clock: Clock,
+    private val authorFactory: AuthorFactory,
+    private val clock: Clock,
     private val groupFactory: GroupFactory,
     private val privateMessageFactory: PrivateMessageFactory,
     private val db: DatabaseComponent,
-    private val identityManager: IdentityManager, private val contactManager: ContactManager,
+    private val identityManager: IdentityManager,
+    private val contactManager: ContactManager,
     private val transportPropertyManager: TransportPropertyManager,
     private val messagingManager: MessagingManager,
     private val testAvatarCreator: TestAvatarCreator,
@@ -58,7 +61,8 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
     private val random = Random()
     private val localAuthors: MutableMap<Contact, LocalAuthor> = HashMap()
     override fun createTestData(
-        numContacts: Int, numPrivateMsgs: Int,
+        numContacts: Int,
+        numPrivateMsgs: Int,
         avatarPercent: Int
     ) {
         require(numContacts != 0)
@@ -66,7 +70,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
         ioExecutor.execute {
             try {
                 createTestDataOnIoExecutor(
-                    numContacts, numPrivateMsgs,
+                    min(numContacts, conversations.persons.size), numPrivateMsgs,
                     avatarPercent
                 )
             } catch (e: DbException) {
@@ -78,7 +82,8 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
     @IoExecutor
     @Throws(DbException::class)
     private fun createTestDataOnIoExecutor(
-        numContacts: Int, numPrivateMsgs: Int,
+        numContacts: Int,
+        numPrivateMsgs: Int,
         avatarPercent: Int
     ) {
         val contacts = createContacts(numContacts, avatarPercent)
@@ -89,12 +94,11 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
     private fun createContacts(numContacts: Int, avatarPercent: Int): List<Contact> {
         val contacts: MutableList<Contact> = ArrayList(numContacts)
         val localAuthor = identityManager.localAuthor
+
         for (i in 0 until numContacts) {
-            val remote = randomAuthor
-            val contact = addContact(
-                localAuthor.id, remote,
-                random.nextBoolean(), avatarPercent
-            )
+            val person = conversations.persons[i]
+            val remote = authorFactory.createLocalAuthor(person.name)
+            val contact = addContact(localAuthor.id, remote, null, avatarPercent)
             contacts.add(contact)
         }
         return contacts
@@ -102,8 +106,10 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
 
     @Throws(DbException::class)
     private fun addContact(
-        localAuthorId: AuthorId, remote: LocalAuthor,
-        alias: Boolean, avatarPercent: Int
+        localAuthorId: AuthorId,
+        remote: LocalAuthor,
+        alias: String?,
+        avatarPercent: Int
     ): Contact {
         // prepare to add contact
         val secretKey = secretKey
@@ -114,44 +120,30 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
         val props = randomTransportProperties
         val contact = db.transactionWithResult<Contact, RuntimeException>(false) { txn: Transaction? ->
             val contactId = contactManager.addContact(
-                txn, remote,
-                localAuthorId, secretKey, timestamp, true, verified, true
+                txn, remote, localAuthorId, secretKey, timestamp, true, verified, true
             )
-            if (alias) {
-                contactManager.setContactAlias(
-                    txn, contactId,
-                    randomAuthorName
-                )
+            if (alias != null) {
+                contactManager.setContactAlias(txn, contactId, alias)
             }
             transportPropertyManager.addRemoteProperties(txn, contactId, props)
             db.getContact(txn, contactId)
         }
         if (random.nextInt(100) + 1 <= avatarPercent) addAvatar(contact)
         if (LOG.isLoggable(Level.INFO)) {
-            LOG.info(
-                "Added contact " + remote.name +
-                        " with transport properties: " + props
-            )
+            LOG.info("Added contact ${remote.name} with transport properties: $props")
         }
         localAuthors[contact] = remote
         return contact
     }
 
     @Throws(DbException::class)
-    override fun addContact(name: String?, alias: Boolean, avatar: Boolean): Contact? {
+    override fun addContact(name: String?, alias: String?, avatar: Boolean): Contact? {
         val localAuthor = identityManager.localAuthor
         val remote = authorFactory.createLocalAuthor(name)
         val avatarPercent = if (avatar) 100 else 0
         return addContact(localAuthor.id, remote, alias, avatarPercent)
     }
 
-    private val randomAuthorName: String
-        private get() {
-            val i = random.nextInt(TestData.AUTHOR_NAMES.size)
-            return TestData.AUTHOR_NAMES[i]
-        }
-    private val randomAuthor: LocalAuthor
-        private get() = authorFactory.createLocalAuthor(randomAuthorName)
     private val secretKey: SecretKey
         private get() {
             val b = ByteArray(SecretKey.LENGTH)
@@ -289,29 +281,29 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
         contacts: List<Contact>,
         numPrivateMsgs: Int
     ) {
-        for (contact in contacts) {
+        for (i in contacts.indices) {
+            val contact = contacts[i]
+            val person = conversations.persons[i]
             val group = messagingManager.getContactGroup(contact)
             shareGroup(contact.id, group.id)
-            for (i in 0 until numPrivateMsgs) {
-                createRandomPrivateMessage(contact.id, group.id, i)
+            for (k in 0 until min(numPrivateMsgs, person.messages.size)) {
+                createPrivateMessage(contact.id, group.id, person.messages[k])
             }
         }
         if (LOG.isLoggable(Level.INFO)) {
-            LOG.info(
-                "Created " + numPrivateMsgs +
-                        " private messages per contact."
-            )
+            LOG.info("Created $numPrivateMsgs private messages per contact.")
         }
     }
 
     @Throws(DbException::class)
-    private fun createRandomPrivateMessage(
+    private fun createPrivateMessage(
         contactId: ContactId,
-        groupId: GroupId, num: Int
+        groupId: GroupId,
+        message: org.briarproject.briar.desktop.testdata.Message
     ) {
-        val timestamp = clock.currentTimeMillis() - num * 60 * 1000
-        val text = randomText
-        val local = random.nextBoolean()
+        val timestamp = message.date.toEpochSecond(ZoneOffset.UTC)
+        val text = message.text
+        val local = message.direction == Direction.OUTGOING
         val autoDelete = random.nextBoolean()
         createPrivateMessage(
             contactId, groupId, text, timestamp, local,
@@ -321,8 +313,12 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
 
     @Throws(DbException::class)
     private fun createPrivateMessage(
-        contactId: ContactId, groupId: GroupId,
-        text: String, timestamp: Long, local: Boolean, autoDelete: Boolean
+        contactId: ContactId,
+        groupId: GroupId,
+        text: String,
+        timestamp: Long,
+        local: Boolean,
+        autoDelete: Boolean
     ) {
         val timer =
             if (autoDelete) AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS else AutoDeleteConstants.NO_AUTO_DELETE_TIMER
-- 
GitLab