Compare commits

...

2 Commits

Author SHA1 Message Date
3e1683dfe5 Use a proper search input parser 2021-03-03 14:28:34 +01:00
51b682c593 Skip slow tests by default 2021-03-03 14:28:34 +01:00
7 changed files with 95 additions and 26 deletions

View File

@ -14,6 +14,6 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31")
implementation("org.jetbrains.kotlin:kotlin-serialization:1.4.31") implementation("org.jetbrains.kotlin:kotlin-serialization:1.4.31")
implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0") implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0")
implementation("org.jlleitschuh.gradle:ktlint-gradle:9.4.1") implementation("org.jlleitschuh.gradle:ktlint-gradle:10.0.0")
implementation("com.github.ben-manes:gradle-versions-plugin:0.28.0") implementation("com.github.ben-manes:gradle-versions-plugin:0.28.0")
} }

View File

@ -5,7 +5,9 @@ plugins {
} }
tasks.withType<Test> { tasks.withType<Test> {
useJUnitPlatform() useJUnitPlatform {
excludeTags("slow")
}
} }
dependencies { dependencies {

View File

@ -1,38 +1,91 @@
package be.simplenotes.domain.usecases.search package be.simplenotes.domain.usecases.search
import be.simplenotes.search.SearchTerms import be.simplenotes.search.SearchTerms
import java.util.*
private fun innerRegex(name: String) = private enum class Quote { SingleQuote, DoubleQuote, }
"""$name:['"](.*?)['"]""".toRegex()
private fun outerRegex(name: String) =
"""($name:['"].*?['"])""".toRegex()
private val titleRe = innerRegex("title") data class ParsedSearchInput(val global: List<String>, val entries: Map<String, String>)
private val outerTitleRe = outerRegex("title")
private val tagRe = innerRegex("tag") object SearchInputParser {
private val outerTagRe = outerRegex("tag") fun parseInput(input: String): ParsedSearchInput {
val tokenizer = StringTokenizer(input, ":\"' ", true)
private val contentRe = innerRegex("content") val tokens = ArrayList<String>()
private val outerContentRe = outerRegex("content") val current = StringBuilder()
var quoteOpen: Quote? = null
fun push() {
if (current.isNotEmpty()) {
tokens.add(current.toString())
}
current.setLength(0)
quoteOpen = null
}
while (tokenizer.hasMoreTokens()) {
when (val token = tokenizer.nextToken()) {
"\"" -> when {
Quote.DoubleQuote == quoteOpen -> push()
quoteOpen == null -> quoteOpen = Quote.DoubleQuote
else -> current.append(token)
}
"'" -> when {
Quote.SingleQuote == quoteOpen -> push()
quoteOpen == null -> quoteOpen = Quote.SingleQuote
else -> current.append(token)
}
" " -> {
if (quoteOpen != null) current.append(" ")
else push()
}
":" -> {
push()
tokens.add(token)
}
else -> {
current.append(token)
}
}
}
push()
val entries = HashMap<String, String>()
val colonIndexes = ArrayList<Int>()
tokens.forEachIndexed { index, token ->
if (token == ":") colonIndexes += index
}
var changes = 0
for (colonIndex in colonIndexes) {
val offset = changes * 3
val key = tokens.getOrNull(colonIndex - 1 - offset)
val value = tokens.getOrNull(colonIndex + 1 - offset)
if (key != null && value != null) {
entries[key] = value
tokens.removeAt(colonIndex - 1 - offset) // remove key
tokens.removeAt(colonIndex - 1 - offset) // remove :
tokens.removeAt(colonIndex - 1 - offset) // remove value
changes++
}
}
return ParsedSearchInput(global = tokens, entries = entries)
}
}
internal fun parseSearchTerms(input: String): SearchTerms { internal fun parseSearchTerms(input: String): SearchTerms {
var c: String = input val parsedInput = SearchInputParser.parseInput(input)
fun extract(innerRegex: Regex, outerRegex: Regex): String? { val title: String? = parsedInput.entries["title"]
val match = innerRegex.find(input)?.groups?.get(1)?.value val tag: String? = parsedInput.entries["tag"]
if (match != null) { val content: String? = parsedInput.entries["content"]
val group = outerRegex.find(input)?.groups?.get(1)?.value
group?.let { c = c.replace(it, "") }
}
return match
}
val title: String? = extract(titleRe, outerTitleRe) val all = parsedInput.global.takeIf { it.isNotEmpty() }?.joinToString(" ")
val tag: String? = extract(tagRe, outerTagRe)
val content: String? = extract(contentRe, outerContentRe)
val all = c.trim().ifEmpty { null }
return SearchTerms( return SearchTerms(
title = title, title = title,

View File

@ -35,6 +35,14 @@ internal class SearchTermsParserKtTest {
tag = "example abc", tag = "example abc",
all = "this is the end" all = "this is the end"
), ),
createResult("tag:blah", tag = "blah"),
createResult("tag:'some words'", tag = "some words"),
createResult("tag:'some words ' global", tag = "some words ", all = "global"),
createResult(
"tag:'double quote inside single \" ' global",
tag = "double quote inside single \" ",
all = "global"
),
) )
@ParameterizedTest @ParameterizedTest

View File

@ -2,6 +2,7 @@ package be.simplenotes.persistence
import be.simplenotes.config.DataSourceConfig import be.simplenotes.config.DataSourceConfig
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.parallel.ResourceLock import org.junit.jupiter.api.parallel.ResourceLock
@ -15,6 +16,7 @@ class H2DbHealthCheckImplTest : DbTest() {
} }
} }
@Tag("slow")
@ResourceLock("mariadb") @ResourceLock("mariadb")
class MariaDbHealthCheckImplTest : DbTest() { class MariaDbHealthCheckImplTest : DbTest() {
lateinit var mariaDB: KMariadbContainer lateinit var mariaDB: KMariadbContainer

View File

@ -5,6 +5,7 @@ import be.simplenotes.persistence.KMariadbContainer
import be.simplenotes.persistence.h2dataSourceConfig import be.simplenotes.persistence.h2dataSourceConfig
import be.simplenotes.persistence.mariadbDataSourceConfig import be.simplenotes.persistence.mariadbDataSourceConfig
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.parallel.ResourceLock import org.junit.jupiter.api.parallel.ResourceLock
@ResourceLock("h2") @ResourceLock("h2")
@ -12,6 +13,7 @@ internal class H2NoteRepositoryImplTests : BaseNoteRepositoryImplTest() {
override fun dataSourceConfig() = h2dataSourceConfig() override fun dataSourceConfig() = h2dataSourceConfig()
} }
@Tag("slow")
@ResourceLock("mariadb") @ResourceLock("mariadb")
internal class MariaDbNoteRepositoryImplTests : BaseNoteRepositoryImplTest() { internal class MariaDbNoteRepositoryImplTests : BaseNoteRepositoryImplTest() {
lateinit var mariaDB: KMariadbContainer lateinit var mariaDB: KMariadbContainer

View File

@ -5,6 +5,7 @@ import be.simplenotes.persistence.KMariadbContainer
import be.simplenotes.persistence.h2dataSourceConfig import be.simplenotes.persistence.h2dataSourceConfig
import be.simplenotes.persistence.mariadbDataSourceConfig import be.simplenotes.persistence.mariadbDataSourceConfig
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.parallel.ResourceLock import org.junit.jupiter.api.parallel.ResourceLock
@ResourceLock("h2") @ResourceLock("h2")
@ -12,6 +13,7 @@ internal class UserRepositoryImplTest : BaseUserRepositoryImplTest() {
override fun dataSourceConfig() = h2dataSourceConfig() override fun dataSourceConfig() = h2dataSourceConfig()
} }
@Tag("slow")
@ResourceLock("mariadb") @ResourceLock("mariadb")
internal class MariaDbUserRepositoryImplTest : BaseUserRepositoryImplTest() { internal class MariaDbUserRepositoryImplTest : BaseUserRepositoryImplTest() {
lateinit var mariaDB: KMariadbContainer lateinit var mariaDB: KMariadbContainer