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-serialization:1.4.31")
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")
}

View File

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

View File

@ -1,38 +1,91 @@
package be.simplenotes.domain.usecases.search
import be.simplenotes.search.SearchTerms
import java.util.*
private fun innerRegex(name: String) =
"""$name:['"](.*?)['"]""".toRegex()
private fun outerRegex(name: String) =
"""($name:['"].*?['"])""".toRegex()
private enum class Quote { SingleQuote, DoubleQuote, }
private val titleRe = innerRegex("title")
private val outerTitleRe = outerRegex("title")
data class ParsedSearchInput(val global: List<String>, val entries: Map<String, String>)
private val tagRe = innerRegex("tag")
private val outerTagRe = outerRegex("tag")
object SearchInputParser {
fun parseInput(input: String): ParsedSearchInput {
val tokenizer = StringTokenizer(input, ":\"' ", true)
private val contentRe = innerRegex("content")
private val outerContentRe = outerRegex("content")
val tokens = ArrayList<String>()
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 {
var c: String = input
val parsedInput = SearchInputParser.parseInput(input)
fun extract(innerRegex: Regex, outerRegex: Regex): String? {
val match = innerRegex.find(input)?.groups?.get(1)?.value
if (match != null) {
val group = outerRegex.find(input)?.groups?.get(1)?.value
group?.let { c = c.replace(it, "") }
}
return match
}
val title: String? = parsedInput.entries["title"]
val tag: String? = parsedInput.entries["tag"]
val content: String? = parsedInput.entries["content"]
val title: String? = extract(titleRe, outerTitleRe)
val tag: String? = extract(tagRe, outerTagRe)
val content: String? = extract(contentRe, outerContentRe)
val all = c.trim().ifEmpty { null }
val all = parsedInput.global.takeIf { it.isNotEmpty() }?.joinToString(" ")
return SearchTerms(
title = title,

View File

@ -35,6 +35,14 @@ internal class SearchTermsParserKtTest {
tag = "example abc",
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

View File

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

View File

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

View File

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