Skip to content

Commit

Permalink
Refactors TestCaseReader to use a simple list instead of implementing…
Browse files Browse the repository at this point in the history
… Iterator interface

Cleanup FlexiblePolyline.Converter and FlexiblePolyline.ThirdDimension as there is no need to use the package name
Cleanup of comments
Using an Exception with more details in case loading a test case failed

Signed-off-by: Hille, Marlon <marlon.hille@here.com>
  • Loading branch information
moo24 committed Jul 22, 2024
1 parent a97d429 commit 034933b
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 63 deletions.
34 changes: 14 additions & 20 deletions kotlin/src/com/here/flexiblepolyline/FlexiblePolyline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import kotlin.math.pow
object FlexiblePolyline {
const val VERSION = 1L

//Base64 URL-safe characters
// Base64 URL-safe characters
private val ENCODING_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray()
private val DECODING_TABLE = intArrayOf(
62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1,
Expand Down Expand Up @@ -82,6 +82,7 @@ object FlexiblePolyline {

/**
* ThirdDimension type from the encoded input [String]
*
* @param encoded URL-safe encoded coordinate triples [String]
* @return type of [ThirdDimension]
*/
Expand All @@ -90,17 +91,15 @@ object FlexiblePolyline {
return Decoder(encoded).thirdDimension
}

//Decode a single char to the corresponding value
// Decode a single char to the corresponding value
private fun decodeChar(charValue: Char): Int {
val pos = charValue.code - 45
return if (pos < 0 || pos > 77) {
-1
} else DECODING_TABLE[pos]
}

/*
* Single instance for configuration, validation and encoding for an input request.
*/
// Single instance for configuration, validation and encoding for an input request.
private class Encoder(precision: Int, private val thirdDimension: ThirdDimension, thirdDimPrecision: Int) {
private val result: StringBuilder = StringBuilder()
private val latConverter: Converter = Converter(precision)
Expand All @@ -112,13 +111,11 @@ object FlexiblePolyline {
}

private fun encodeHeader(precision: Int, thirdDimensionValue: Int, thirdDimPrecision: Int) {
/*
* Encode the `precision`, `third_dim` and `third_dim_precision` into one encoded char
*/
// Encode the `precision`, `third_dim` and `third_dim_precision` into one encoded char
require(!(precision < 0 || precision > 15)) { "precision out of range" }
require(!(thirdDimPrecision < 0 || thirdDimPrecision > 15)) { "thirdDimPrecision out of range" }
require(!(thirdDimensionValue < 0 || thirdDimensionValue > 7)) { "thirdDimensionValue out of range" }
val res = (thirdDimPrecision shl 7 or (thirdDimensionValue shl 4) or precision).toLong()
val res = ((thirdDimPrecision shl 7) or (thirdDimensionValue shl 4) or precision).toLong()
Converter.encodeUnsignedVarInt(VERSION, result)
Converter.encodeUnsignedVarInt(res, result)
}
Expand All @@ -145,9 +142,7 @@ object FlexiblePolyline {
}
}

/*
* Single instance for decoding an input request.
*/
// Single instance for decoding an input request.
private class Decoder(encoded: String) : Iterator<LatLngZ> {
private val encoded: CharIterator = encoded.iterator()
private val latConverter: Converter
Expand Down Expand Up @@ -192,11 +187,13 @@ object FlexiblePolyline {
}
}

/*
/**
* Stateful instance for encoding and decoding on a sequence of Coordinates part of an request.
* Instance should be specific to type of coordinates (e.g. Lat, Lng)
* so that specific type delta is computed for encoding.
* Lat0 Lng0 3rd0 (Lat1-Lat0) (Lng1-Lng0) (3rdDim1-3rdDim0)
*
* @param precision [Int]
*/
class Converter(precision: Int) {
private val multiplier = (10.0.pow(precision.toDouble())).toLong()
Expand Down Expand Up @@ -224,7 +221,7 @@ object FlexiblePolyline {
encodeUnsignedVarInt(delta, result)
}

//Decode single coordinate (say lat|lng|z) starting at index
// Decode single coordinate (say lat|lng|z) starting at index
fun decodeValue(encoded: CharIterator): Double {
var l = decodeUnsignedVarInt(encoded)
if ((l and 1L) != 0L) {
Expand All @@ -251,10 +248,10 @@ object FlexiblePolyline {
var shift: Short = 0
var result: Long = 0

encoded.forEach {
val value = decodeChar(it).toLong()
encoded.withIndex().forEach {
val value = decodeChar(it.value).toLong()
if (value < 0) {
throw IllegalArgumentException("Unexpected value found :: '$it")
throw IllegalArgumentException("Unexpected value found :: '${it.value}' at ${it.index}")
}
result = result or ((value and 0x1FL) shl shift.toInt())
if ((value and 0x20L) == 0L) {
Expand Down Expand Up @@ -288,9 +285,6 @@ object FlexiblePolyline {
}
}

/**
* Coordinate triple
*/
/**
* Coordinate triple
*/
Expand Down
78 changes: 35 additions & 43 deletions kotlin/src/com/here/flexiblepolyline/FlexiblePolylineTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT
* License-Filename: LICENSE
*/
import com.here.flexiblepolyline.*
import com.here.flexiblepolyline.FlexiblePolyline
import com.here.flexiblepolyline.FlexiblePolyline.Converter
import com.here.flexiblepolyline.FlexiblePolyline.ThirdDimension
import com.here.flexiblepolyline.FlexiblePolyline.LatLngZ
Expand All @@ -18,13 +18,13 @@ import java.util.*
class FlexiblePolylineTest {
private fun testInvalidCoordinates() {

//Null coordinates
// Null coordinates
assertThrows(
IllegalArgumentException::class.java
) { FlexiblePolyline.encode(null, 5, ThirdDimension.ABSENT, 0) }


//Empty coordinates list test
// Empty coordinates list test
assertThrows(
IllegalArgumentException::class.java
) { FlexiblePolyline.encode(ArrayList<LatLngZ>(), 5, ThirdDimension.ABSENT, 0) }
Expand All @@ -35,14 +35,14 @@ class FlexiblePolylineTest {
pairs.add(LatLngZ(50.1022829, 8.6982122))
val invalid: ThirdDimension? = null

//Invalid Third Dimension
// Invalid Third Dimension
assertThrows(
IllegalArgumentException::class.java
) { FlexiblePolyline.encode(pairs, 5, invalid, 0) }
}

private fun testConvertValue() {
val conv: FlexiblePolyline.Converter = Converter(5)
val conv: Converter = Converter(5)
val result = StringBuilder()
conv.encodeValue(-179.98321, result)
assertEquals(result.toString(), "h_wqiB")
Expand Down Expand Up @@ -86,18 +86,19 @@ class FlexiblePolylineTest {
val computed: String = FlexiblePolyline.encode(tuples, 5, ThirdDimension.ALTITUDE, 0)
assertEquals(computed, expected)
}
/** */
/********** Decoder test starts */
/** */

/**
* Decoder test starts
*/
private fun testInvalidEncoderInput() {

//Null coordinates
// Null coordinates
assertThrows(
IllegalArgumentException::class.java
) { FlexiblePolyline.decode(null) }


//Empty coordinates list test
// Empty coordinates list test
assertThrows(
IllegalArgumentException::class.java
) { FlexiblePolyline.decode("") }
Expand Down Expand Up @@ -164,7 +165,7 @@ class FlexiblePolylineTest {
}

private fun encodingSmokeTest() {
TestCaseReader("original.txt", "round_half_up/encoded.txt").iterator().forEach {
TestCaseReader("original.txt", "round_half_up/encoded.txt").testCases.forEach {
val original = parseTestDataFromLine(it.testInput);
val encodedComputed: String = FlexiblePolyline.encode(original.latLngZs, original.precision, original.thirdDimension, original.thirdDimensionPrecision)

Expand All @@ -173,14 +174,14 @@ class FlexiblePolylineTest {
}

private fun decodingSmokeTest() {
TestCaseReader("round_half_up/encoded.txt", "round_half_up/decoded.txt").iterator().forEach {
TestCaseReader("round_half_up/encoded.txt", "round_half_up/decoded.txt").testCases.forEach {
val expected = parseTestDataFromLine(it.testResult);

//Validate thirdDimension
val computedDimension: FlexiblePolyline.ThirdDimension = FlexiblePolyline.getThirdDimension(it.testInput)!!
// Validate thirdDimension
val computedDimension: ThirdDimension = FlexiblePolyline.getThirdDimension(it.testInput)!!
assertEquals(computedDimension, expected.thirdDimension)

//Validate LatLngZ
// Validate LatLngZ
val computedLatLngZs: List<FlexiblePolyline.LatLngZ> = FlexiblePolyline.decode(it.testInput)

assertEquals(computedLatLngZs.size, expected.latLngZs?.size)
Expand Down Expand Up @@ -211,23 +212,25 @@ class FlexiblePolylineTest {
companion object {
const val TEST_FILES_RELATIVE_PATH = "../test/"

// Helper for parsing DecodeLines file
// Line Format: {(precision, thirdDimPrecision?, thirdDim?); [(c1Lat, c1Lng, c1Alt), ]}
/*
* Helper for parsing DecodeLines file
* Line Format: {(precision, thirdDimPrecision?, thirdDim?); [(c1Lat, c1Lng, c1Alt), ]}
*/
private fun parseTestDataFromLine(line: String): TestData {
var precision = 0
var thirdDimensionPrecision = 0
var hasThirdDimension = false
var thirdDimension: FlexiblePolyline.ThirdDimension? = FlexiblePolyline.ThirdDimension.ABSENT
var thirdDimension: ThirdDimension? = FlexiblePolyline.ThirdDimension.ABSENT

// .substring gets rid of { and }
val splitBySemicolon = line.substring(1, line.length - 1).split(";").toTypedArray();
val leftPart = splitBySemicolon[0];
val meta = leftPart.split(",").toTypedArray();
precision = Integer.valueOf(meta[0])
if (meta.size > 1) {
thirdDimension = FlexiblePolyline.ThirdDimension.fromNum(Integer.valueOf(meta[2].trim()).toLong())
thirdDimension = ThirdDimension.fromNum(Integer.valueOf(meta[2].trim()).toLong())
hasThirdDimension = true
thirdDimensionPrecision = Integer.valueOf(meta[1].trim { it <= ' ' })
thirdDimensionPrecision = Integer.valueOf(meta[1].trim())
}

val latLngZs = extractLatLngZ(splitBySemicolon[1], hasThirdDimension)
Expand Down Expand Up @@ -256,10 +259,6 @@ class FlexiblePolylineTest {
return latLngZs
}

private fun isNullOrEmpty(str: String?): Boolean {
return str == null || str.trim().isEmpty()
}

private fun assertEquals(lhs: Any, rhs: Any?) {
if (lhs !== rhs) {
if (lhs != rhs) {
Expand Down Expand Up @@ -306,7 +305,7 @@ class FlexiblePolylineTest {
test.testLatLngZEncode()
test.encodingSmokeTest()

//Decode test
// Decode test
test.testInvalidEncoderInput()
test.testThirdDimension()
test.testDecodeConvertValue()
Expand All @@ -322,7 +321,7 @@ class FlexiblePolylineTest {
private data class TestData(
val precision: Int = 0,
val thirdDimensionPrecision: Int = 0,
val thirdDimension: FlexiblePolyline.ThirdDimension? = null,
val thirdDimension: ThirdDimension? = null,
val latLngZs: List<FlexiblePolyline.LatLngZ>? = null
){}

Expand All @@ -331,12 +330,12 @@ private data class TestCase(
val testResult: String
){}

private class TestCaseReader(testInputFile: String, testResultFile: String) : Iterator<TestCase> {
private var totalLines = 0
private var currentLine = 0
private var testCases = mutableListOf<TestCase>()
private class TestCaseReader(testInputFile: String, testResultFile: String) {
val testCases: List<TestCase>

init {
var totalLines = 0
val testCaseList = mutableListOf<TestCase>()
try {
Files.newBufferedReader(Paths.get(FlexiblePolylineTest.TEST_FILES_RELATIVE_PATH + testInputFile)).use { input ->
Files.newBufferedReader(Paths.get(FlexiblePolylineTest.TEST_FILES_RELATIVE_PATH + testResultFile)).use { result ->
Expand All @@ -347,14 +346,17 @@ private class TestCaseReader(testInputFile: String, testResultFile: String) : It
val testResultFileLine = result.readLine();

if (testInputFileLine != null && testInputFileLine.isNotBlank() && testResultFileLine != null && testResultFileLine.isNotBlank()) {
testCases.add(
testCaseList.add(
TestCase(
testInput = testInputFileLine.replace(regex, ""),
testResult = testResultFileLine.replace(regex, "")
)
)
totalLines++
} else {
if(totalLines==0) {
System.err.format("TestCaseReader - 0 test cases in file $testInputFile or $testResultFile:")
}
break
}
}
Expand All @@ -363,18 +365,8 @@ private class TestCaseReader(testInputFile: String, testResultFile: String) : It
} catch (e: Exception) {
e.printStackTrace()
System.err.format("TestCaseReader - exception reading test case $testInputFile and $testResultFile at LineNo: $totalLines: %s%n", e)
throw RuntimeException("Test failed, as test data could not be loaded by TestCaseReader")
throw RuntimeException("TestCaseReader - exception reading test case $testInputFile and $testResultFile at LineNo: $totalLines", e)
}
}

override fun hasNext(): Boolean {
return currentLine < totalLines
}

override fun next(): TestCase {
if(!hasNext()) throw NoSuchElementException()
val testCase = testCases[currentLine]
currentLine++
return testCase
testCases = testCaseList.toList()
}
}

0 comments on commit 034933b

Please sign in to comment.