commonTest.io.islandtime.parser.DecimalNumberParserTest.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-metadata Show documentation
Show all versions of core-metadata Show documentation
A multiplatform library for working with dates and times
The newest version!
package io.islandtime.parser
import io.islandtime.base.DateTimeField
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class DecimalNumberParserTest {
@Test
fun `throws an exception if whole range is empty`() {
assertFailsWith {
dateTimeParser { decimalNumber(wholeLength = IntRange.EMPTY) }
}
}
@Test
fun `throws an exception if fraction range is empty`() {
assertFailsWith {
dateTimeParser { decimalNumber(fractionLength = IntRange.EMPTY) }
}
}
@Test
fun `throws an exception if whole range is outside of 0-19`() {
assertFailsWith {
dateTimeParser { decimalNumber(wholeLength = -1..4) }
}
assertFailsWith {
dateTimeParser { decimalNumber(wholeLength = 5..20) }
}
}
@Test
fun `throws an exception if fraction range is outside of 0-9`() {
assertFailsWith {
dateTimeParser { decimalNumber(fractionLength = -1..4) }
}
assertFailsWith {
dateTimeParser { decimalNumber(fractionLength = 5..10) }
}
}
@Test
fun `throws an exception if fraction scale is outside of 1-9`() {
assertFailsWith {
dateTimeParser { decimalNumber(fractionScale = 0) }
}
assertFailsWith {
dateTimeParser { decimalNumber(fractionScale = 10) }
}
}
@Test
fun `parses decimal numbers into two components`() {
val parser = dateTimeParser {
decimalNumber {
onParsed { whole, fraction ->
fields[DateTimeField.SECOND_OF_MINUTE] = whole
fields[DateTimeField.NANOSECOND_OF_SECOND] = fraction
}
}
+' '
}
val result1 = parser.parse("0.1 ")
assertEquals(0L, result1.fields[DateTimeField.SECOND_OF_MINUTE])
assertEquals(100_000_000L, result1.fields[DateTimeField.NANOSECOND_OF_SECOND])
val result2 = parser.parse("-5.000000001 ")
assertEquals(-5L, result2.fields[DateTimeField.SECOND_OF_MINUTE])
assertEquals(-1L, result2.fields[DateTimeField.NANOSECOND_OF_SECOND])
val result3 = parser.parse("10 ")
assertEquals(10L, result3.fields[DateTimeField.SECOND_OF_MINUTE])
assertEquals(0L, result3.fields[DateTimeField.NANOSECOND_OF_SECOND])
}
@Test
fun `allows decimal numbers with a zero length whole component`() {
val parser = dateTimeParser {
decimalNumber(0..19) {
onParsed { whole, fraction ->
fields[DateTimeField.SECOND_OF_MINUTE] = whole
fields[DateTimeField.NANOSECOND_OF_SECOND] = fraction
}
}
}
val result1 = parser.parse(".1")
assertEquals(0L, result1.fields[DateTimeField.SECOND_OF_MINUTE])
assertEquals(100_000_000L, result1.fields[DateTimeField.NANOSECOND_OF_SECOND])
val result2 = parser.parse("-.000000001")
assertEquals(0L, result2.fields[DateTimeField.SECOND_OF_MINUTE])
assertEquals(-1L, result2.fields[DateTimeField.NANOSECOND_OF_SECOND])
}
@Test
fun `enforces whole length`() {
val parser = dateTimeParser {
decimalNumber(wholeLength = 2..3)
}
listOf(
"4",
"0.3",
"4000.02",
"-1.0004"
).forEach {
assertFailsWith { parser.parse(it) }
}
}
@Test
fun `enforces fraction length`() {
val parser1 = dateTimeParser {
decimalNumber(fractionLength = 1..3)
}
listOf(
"45",
"-34",
"0.0004",
"-1.0001",
"100-",
"0.",
"0/0"
).forEach {
assertFailsWith { parser1.parse(it) }
}
val parser2 = dateTimeParser {
decimalNumber(fractionLength = 2..4)
+' '
}
listOf(
"0.1 ",
"0.12345 ",
"0.1234567890 "
).forEach {
assertFailsWith { parser2.parse(it) }
}
val parser3 = dateTimeParser {
decimalNumber(fractionLength = 0..0)
+' '
}
val exception = assertFailsWith { parser3.parse("0.1 ") }
assertEquals(1, exception.errorIndex)
}
@Test
fun `fractionScale controls the magnitude of the fractional part`() {
val parser = dateTimeParser {
decimalNumber(fractionScale = 3) {
onParsed { _, fraction -> fields[DateTimeField.MILLISECOND_OF_SECOND] = fraction }
}
}
assertEquals(300L, parser.parse("0.3").fields[DateTimeField.MILLISECOND_OF_SECOND])
}
@Test
fun `reports an error when there are no characters to parse`() {
val parser = dateTimeParser {
+' '
decimalNumber()
}
val exception = assertFailsWith { parser.parse(" ") }
assertEquals(1, exception.errorIndex)
assertEquals(" ", exception.parsedString)
}
@Test
fun `reports an error if the whole and fractional parts are both absent`() {
val parser1 = dateTimeParser {
decimalNumber(0..19) {
onParsed { whole, fraction ->
fields[DateTimeField.SECOND_OF_MINUTE] = whole
fields[DateTimeField.NANOSECOND_OF_SECOND] = fraction
}
}
}
listOf(
"-",
"+",
"-.",
"+.",
"."
).forEach {
val exception = assertFailsWith { parser1.parse(it) }
assertEquals(it.length, exception.errorIndex)
}
val parser2 = dateTimeParser {
childParser(parser1)
+' '
}
listOf(
"- ",
"+ ",
"-. ",
"+. ",
". "
).forEach {
val exception = assertFailsWith { parser2.parse(it) }
assertEquals(it.length - 1, exception.errorIndex)
}
}
@Test
fun `reports an error when a trailing decimal separator is found`() {
val parser = dateTimeParser { decimalNumber() }
val exception = assertFailsWith { parser.parse("-4.") }
assertEquals(3, exception.errorIndex)
}
@Test
fun `throws an exception on overflow`() {
val parser = dateTimeParser {
+' '
decimalNumber {
onParsed { whole, _ ->
fields[DateTimeField.DURATION_OF_HOURS] = whole
}
}
}
listOf(
" 9223372036854775808",
" -9223372036854775809",
" +9300000000000000000"
).forEach {
val exception = assertFailsWith { parser.parse(it) }
assertEquals(1, exception.errorIndex)
assertEquals(it, exception.parsedString)
assertTrue { exception.cause is ArithmeticException }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy