Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/detekt/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ complexity:
ignoreArgumentsMatchingNames: false
NestedBlockDepth:
active: true
threshold: 4
threshold: 6
NestedScopeFunctions:
active: false
threshold: 1
Expand Down
62 changes: 62 additions & 0 deletions src/main/kotlin/utils/SmartWrap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package utils

import org.jetbrains.annotations.VisibleForTesting
import utils.SmartWrap.Companion.canFitOnALine
import java.lang.System.lineSeparator

class SmartWrap(
private val maxCharsPerLine: Int,
private val lineSeparator: String = lineSeparator(),
) {
operator fun invoke(input: String): String =
with(input) {
if (length <= maxCharsPerLine) {
return this
}
val words = split("\\s".toRegex())
var paragraph = ""
words.forEach { word ->
if (paragraph.wouldOverFlow(maxCharsPerLine, word)) {
if (paragraph.isNotEmpty()) paragraph += lineSeparator
paragraph +=
if (word.canFitOnALine(maxCharsPerLine)) {
word
} else {
word.chunked(maxCharsPerLine - 1).joinToString(lineSeparator) { "$it-" }.trimEnd('-')
}
} else {
paragraph += word
}
paragraph += if (paragraph.isLineFull(maxCharsPerLine)) lineSeparator else " "
}
return paragraph.trimEnd()
}

companion object {
@VisibleForTesting
fun String.isSingleLineParagraph() = !contains(lineSeparator())

@VisibleForTesting
fun String.canFitOnALine(maxCharsPerLine: Int) = length <= maxCharsPerLine

@VisibleForTesting
fun String.wouldOverFlow(
maxCharsPerLine: Int,
word: String,
) = charactersUsedInLastLine() + word.length > maxCharsPerLine

@VisibleForTesting
fun String.charactersUsedInLastLine(): Int =
if (isSingleLineParagraph()) {
length
} else {
lastIndex - (lastIndexOf(lineSeparator()) + (lineSeparator().length - 1))
}

@VisibleForTesting
fun String.charactersRemainingInLastLine(maxCharsPerLine: Int) = maxCharsPerLine - charactersUsedInLastLine()

@VisibleForTesting
fun String.isLineFull(maxCharsPerLine: Int) = charactersRemainingInLastLine(maxCharsPerLine) <= 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.equals.shouldBeEqual
import org.junit.jupiter.api.Test

class StringUtilsKtTest {
class CutOffKtTest {
@Test
fun `when string is less than max character limit, then return same string`() {
val input = "testyy"
Expand Down
70 changes: 70 additions & 0 deletions src/test/kotlin/utils/SmartWrapKtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package utils
import io.kotest.matchers.equals.shouldBeEqual
import io.kotest.matchers.string.shouldBeEmpty
import org.junit.jupiter.api.Test
import java.lang.System.lineSeparator

class SmartWrapKtTest {
@Test
fun `given empty String, when cutoff size is 0, then return empty string`() {
"".smartWrap(0).shouldBeEmpty()
}

@Test
fun `when a String shorter than the cutoff size, then return original string`() {
"ciao".smartWrap(10).shouldBeEqual("ciao")
}

@Test
fun `when string is longer than cutoff size, and the string contains no white space, then return split string with hyphenation`() {
"loooongStriiing".smartWrap(10).shouldBeEqual("loooongSt-${lineSeparator()}riiing")
}

@Test
fun `when string with no spaces is longer than twice maxCharPerLine, then return string split in 3 lines with hyphens`() {
val myString = "1234567890123"
myString.smartWrap(6).shouldBeEqual("12345-${lineSeparator()}67890-${lineSeparator()}123")
}

@Test
fun `when string with spaces is longer than maxCharPerLine, then return string split in 2 lines on spaces`() {
val myString = "12345 789012"
myString.smartWrap(6).shouldBeEqual("12345 ${lineSeparator()}789012")
}

@Test
fun `given last line in multiline paragraph is completely full, when appending string, then append string to new line without preceding space`() {
val myString = "123456 789012"
myString.smartWrap(6).shouldBeEqual("123456${lineSeparator()}789012")
}

@Test
fun `when string with spaces is longer than maxCharPerLine, and second word is longer than maxCharPerLine, then return string split in 3 lines`() {
val myString = "12345 8901234567890"
myString.smartWrap(
6,
).shouldBeEqual("12345 ${lineSeparator()}89012-${lineSeparator()}34567-${lineSeparator()}890")
}

@Test
fun `given sting contains multiple contiguous spaces, when total paragraph length is less than maxCharPerLine, then return paragraph`() {
val myString = "12345 89012"
myString.smartWrap(20).shouldBeEqual("12345 89012")
}

@Test
fun `given sting contains multiple contiguous spaces, when total paragraph length is more than maxCharPerLine, then return paragraph wrapped`() {
val myString = "12345 89012"
myString.smartWrap(10).shouldBeEqual("12345 ${lineSeparator()}89012")
}

@Test
fun `given sting contains multiple contiguous spaces, when total paragraph length is more than maxCharPerLine, and number of spaces overflows line length, then return wrapped paragraph with new line consuming one space`() {
val myString = "12345 89012"
myString.smartWrap(10).shouldBeEqual("12345 ${lineSeparator()} 89012")
}

companion object {
private fun String.smartWrap(maxCharsPerLine: Int): String = SmartWrap(maxCharsPerLine).invoke(this)
}
}
152 changes: 152 additions & 0 deletions src/test/kotlin/utils/StringUtilsFunctionsTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package utils
import io.kotest.matchers.booleans.shouldBeFalse
import io.kotest.matchers.booleans.shouldBeTrue
import io.kotest.matchers.equals.shouldBeEqual
import org.junit.jupiter.api.Test
import utils.SmartWrap.Companion.canFitOnALine
import utils.SmartWrap.Companion.charactersUsedInLastLine
import utils.SmartWrap.Companion.isLineFull
import utils.SmartWrap.Companion.isSingleLineParagraph
import utils.SmartWrap.Companion.wouldOverFlow
import java.lang.System.lineSeparator

class StringUtilsFunctionsTest {
@Test
fun `given empty paragraph, when isSingleLineParagraph, then return true`() {
"".isSingleLineParagraph().shouldBeTrue()
}

@Test
fun `given non empty paragraph when is single line paragraph, then return true`() {
"ciao".isSingleLineParagraph().shouldBeTrue()
}

@Test
fun `given non empty paragraph and text contains new line break, then return false`() {
"ciao${lineSeparator()}ciao".isSingleLineParagraph().shouldBeFalse()
}

@Test
fun `given non empty paragraph that starts with end of line break, then return false`() {
"${lineSeparator()}ciao".isSingleLineParagraph().shouldBeFalse()
}

@Test
fun `given empty paragraph, when charactersUsedInLastLne then return 0`() {
"".charactersUsedInLastLine() shouldBeEqual 0
}

@Test
fun `given single line paragraph, when charactersUsedInLastLne then return length of input`() {
"ciao".charactersUsedInLastLine() shouldBeEqual 4
}

@Test
fun `given 2 line paragraph, when charactersUsedInLastLne then return length of last line`() {
val myString = "ciao${lineSeparator()}ciaoo"
println(myString.length)
println(myString.lastIndexOf(lineSeparator()))
myString.charactersUsedInLastLine() shouldBeEqual 5
}

@Test
fun `given multiple line paragraph, when charactersUsedInLastLne then return length of last line`() {
val myString = "ciaooo${lineSeparator()}ciao${lineSeparator()}ciaoo"
myString.charactersUsedInLastLine() shouldBeEqual 5
}

@Test
fun `given single line paragraph and non empty word, and total length is equal to max character count limit, when wouldOverflow, then return False`() {
val paragraph = "1234"
val myString = "567891"
paragraph.wouldOverFlow(10, myString).shouldBeFalse()
}

@Test
fun `given single line paragraph and non empty word, and total length is more than max character count limit, when wouldOverflow, then return True`() {
val paragraph = "paragraph"
val myString = "word"
paragraph.wouldOverFlow(10, myString).shouldBeTrue()
}

@Test
fun `given single line paragraph and non empty word, and total length is less than max character count limit, then return False`() {
val paragraph = "paragraph"
val myString = "word"
paragraph.wouldOverFlow(200, myString).shouldBeFalse()
}

@Test
fun `given multiple line paragraph and non empty word, and last line length plus word length is less than max character count limit, then return False`() {
val paragraph = "ciaociao${lineSeparator()}ciao"
val myString = "word"
paragraph.wouldOverFlow(10, myString).shouldBeFalse()
}

@Test
fun `given multiple line paragraph and non empty word, and last line length plus word length is more than max character count limit, then return True`() {
val paragraph = "ciao${lineSeparator()}ciaociao"
val myString = "word"
paragraph.wouldOverFlow(10, myString).shouldBeTrue()
}

@Test
fun `given empty string, and maxChar 0, when isLineFull, then return True`() {
"".isLineFull(0).shouldBeTrue()
}

@Test
fun `given empty string, and maxChar greater than 0, when isLineFull, then return False`() {
"".isLineFull(10).shouldBeFalse()
}

@Test
fun `given non empty string, and maxChar is 0, when isLineFull, then return True`() {
"ciao".isLineFull(0).shouldBeTrue()
}

@Test
fun `given non empty string, and maxChar greater than string length, when isLineFull, then return False`() {
"ciao".isLineFull(10).shouldBeFalse()
}

@Test
fun `given multiline string, and maxChar greater than last line length, when isLineFull, then return False`() {
"ciao${lineSeparator()}ciao".isLineFull(10).shouldBeFalse()
}

@Test
fun `given multiline string, and maxChar equal to last line length, when isLineFull, then return False`() {
"ciao${lineSeparator()}ciao".isLineFull(4).shouldBeTrue()
}

@Test
fun `given multiline string, and maxChar less than last line length, when isLineFull, then return False`() {
"ciao${lineSeparator()}ciao".isLineFull(4).shouldBeTrue()
}

@Test
fun `given empty string, and maxChar is zero, when canFitOnALine, then return True`() {
"".canFitOnALine(0).shouldBeTrue()
}

@Test
fun `given non empty string, and maxChar is zero, when canFitOnALine, then return False`() {
"ciao".canFitOnALine(0).shouldBeFalse()
}

@Test
fun `given non empty string, and maxChar is greater than string length, when canFitOnALine, then return True`() {
"ciao".canFitOnALine(6).shouldBeTrue()
}

@Test
fun `given non empty string, and maxChar is equal to string length, when canFitOnALine, then return True`() {
"ciao".canFitOnALine(4).shouldBeTrue()
}

@Test
fun `given non empty string, and maxChar is less than string length, when canFitOnALine, then return False`() {
"ciao".canFitOnALine(3).shouldBeFalse()
}
}
Loading