diff --git a/.gitignore b/.gitignore index deb7f38..a9e060a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ replay_pid* .gradle .idea build/ + +# Benchmark result files +benchmark_results_*.csv +benchmark_summary_*.txt diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 0000000..ec69b77 --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,69 @@ +# JSON5 vs JSON Performance Benchmark + +This module provides benchmarking tools to compare the performance of JSON5 serialization/deserialization against standard JSON using kotlinx.serialization. + +## Running the Benchmark + +To run the complete benchmark suite: + +```bash +./gradlew :benchmark:run +``` + +This will: +1. Warm up the JVM +2. Run serialization and deserialization benchmarks for various data types +3. Generate CSV and summary reports + +## Generated Reports + +The benchmark generates two types of reports: + +### CSV Report +- File: `benchmark_results_YYYY-MM-DD_HH-mm-ss.csv` +- Contains detailed timing data for each test +- Suitable for importing into spreadsheet applications or data analysis tools +- Columns: Operation, DataType, Format, Iterations, TotalTimeNanos, AverageTimeNanos, AverageTimeMicros, AverageTimeMillis + +### Summary Report +- File: `benchmark_summary_YYYY-MM-DD_HH-mm-ss.txt` +- Contains human-readable performance comparisons +- Shows which format is faster and by how much +- Includes overall statistics + +## Test Data Types + +The benchmark tests the following data structures: + +- **SimplePerson**: Basic data class with name, age, and boolean +- **ComplexPerson**: Complex nested object with address, phone numbers, skills, etc. +- **Company**: Large nested structure with employees and departments +- **NumberTypes**: Various numeric types (int, long, double, float, byte, short) +- **CollectionTypes**: Lists, maps, and nested collections +- **Lists**: Collections of 50-100 complex objects + +## Configuration + +You can modify the benchmark parameters in `BenchmarkRunner.kt`: + +- `iterations`: Number of operations per test (default: 1000) +- `warmupIterations`: Number of warmup iterations (default: 100) + +## Running Tests + +To run the benchmark module tests: + +```bash +./gradlew :benchmark:test +``` + +## Sample Results + +Based on typical runs, JSON standard library generally performs 2-6x faster than JSON5 for both serialization and deserialization, with the performance gap being larger for more complex data structures. + +Example output: +``` +SimplePerson Serialization: JSON5=0.027ms, JSON=0.013ms +ComplexPerson Serialization: JSON5=0.083ms, JSON=0.015ms +Company Serialization: JSON5=0.200ms, JSON=0.032ms +``` \ No newline at end of file diff --git a/benchmark/build.gradle.kts b/benchmark/build.gradle.kts new file mode 100644 index 0000000..e94d037 --- /dev/null +++ b/benchmark/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + // Apply the shared build logic from a convention plugin. + id("buildsrc.convention.kotlin-jvm") + alias(libs.plugins.kotlinPluginSerialization) + application +} + +dependencies { + // Depend on the lib module for JSON5 implementation + implementation(project(":lib")) + + // kotlinx.serialization for JSON comparison + implementation(libs.bundles.kotlinxEcosystem) + + // Test dependencies + testImplementation(kotlin("test")) + testImplementation("org.junit.jupiter:junit-jupiter:5.10.1") +} + +application { + // Set the main class for the benchmark application + mainClass.set("dev.hossain.json5kt.benchmark.BenchmarkRunner") +} + +tasks.test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/BenchmarkRunner.kt b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/BenchmarkRunner.kt new file mode 100644 index 0000000..11a573b --- /dev/null +++ b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/BenchmarkRunner.kt @@ -0,0 +1,170 @@ +package dev.hossain.json5kt.benchmark + +import java.io.File +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +/** + * Main class to run JSON5 vs JSON serialization benchmarks and generate CSV reports. + */ +object BenchmarkRunner { + + @JvmStatic + fun main(args: Array) { + println("Starting JSON5 vs JSON Serialization Benchmarks...") + println("===============================================") + + val benchmark = SerializationBenchmark() + val allResults = mutableListOf() + + // Configuration + val iterations = 1000 + val warmupIterations = 100 + + println("Configuration:") + println("- Iterations per test: $iterations") + println("- Warmup iterations: $warmupIterations") + println() + + // Warmup JVM + println("Warming up JVM...") + runWarmup(benchmark, warmupIterations) + + // Run benchmarks for different data types + println("Running benchmarks...") + + // Simple Person + println("Benchmarking SimplePerson...") + val simplePerson = TestDataGenerator.createSimplePerson() + allResults.addAll(benchmark.benchmarkComplete(simplePerson, "SimplePerson", iterations, SimplePerson.serializer())) + + // Complex Person + println("Benchmarking ComplexPerson...") + val complexPerson = TestDataGenerator.createComplexPerson() + allResults.addAll(benchmark.benchmarkComplete(complexPerson, "ComplexPerson", iterations, ComplexPerson.serializer())) + + // Company (large nested object) + println("Benchmarking Company...") + val company = TestDataGenerator.createCompany() + allResults.addAll(benchmark.benchmarkComplete(company, "Company", iterations, Company.serializer())) + + // Number Types + println("Benchmarking NumberTypes...") + val numberTypes = TestDataGenerator.createNumberTypes() + allResults.addAll(benchmark.benchmarkComplete(numberTypes, "NumberTypes", iterations, NumberTypes.serializer())) + + // Collection Types + println("Benchmarking CollectionTypes...") + val collectionTypes = TestDataGenerator.createCollectionTypes() + allResults.addAll(benchmark.benchmarkComplete(collectionTypes, "CollectionTypes", iterations, CollectionTypes.serializer())) + + // List of Simple Persons (100 items) + println("Benchmarking List of 100 SimplePersons...") + val simplePersonList = TestDataGenerator.createSimplePersonList(100) + allResults.addAll(benchmark.benchmarkComplete(simplePersonList, "SimplePersonList100", iterations, kotlinx.serialization.builtins.ListSerializer(SimplePerson.serializer()))) + + // List of Complex Persons (50 items) + println("Benchmarking List of 50 ComplexPersons...") + val complexPersonList = TestDataGenerator.createComplexPersonList(50) + allResults.addAll(benchmark.benchmarkComplete(complexPersonList, "ComplexPersonList50", iterations, kotlinx.serialization.builtins.ListSerializer(ComplexPerson.serializer()))) + + // Generate reports + println() + println("Generating reports...") + generateCsvReport(allResults) + generateSummaryReport(allResults) + + println("Benchmarks completed!") + } + + private fun runWarmup(benchmark: SerializationBenchmark, warmupIterations: Int) { + val warmupData = TestDataGenerator.createSimplePerson() + benchmark.benchmarkComplete(warmupData, "Warmup", warmupIterations, SimplePerson.serializer()) + } + + private fun generateCsvReport(results: List) { + val timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss")) + val filename = "benchmark_results_$timestamp.csv" + + File(filename).writeText( + buildString { + appendLine(SerializationBenchmark.BenchmarkResult.csvHeader()) + results.forEach { result -> + appendLine(result.toCsvRow()) + } + } + ) + + println("CSV report generated: $filename") + } + + private fun generateSummaryReport(results: List) { + val timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss")) + val filename = "benchmark_summary_$timestamp.txt" + + val report = buildString { + appendLine("JSON5 vs JSON Serialization Benchmark Summary") + appendLine("===========================================") + appendLine("Generated: ${LocalDateTime.now()}") + appendLine() + + // Group results by data type and operation + val groupedResults = results.groupBy { "${it.dataType}_${it.operation}" } + + groupedResults.forEach { (key, resultList) -> + val (dataType, operation) = key.split("_") + appendLine("$dataType - $operation:") + + val json5Result = resultList.find { it.format == "JSON5" } + val jsonResult = resultList.find { it.format == "JSON" } + + if (json5Result != null && jsonResult != null) { + appendLine(" JSON5: ${String.format("%.3f", json5Result.averageTimeMillis)} ms avg") + appendLine(" JSON: ${String.format("%.3f", jsonResult.averageTimeMillis)} ms avg") + + val speedup = json5Result.averageTimeMillis / jsonResult.averageTimeMillis + if (speedup > 1.0) { + appendLine(" → JSON is ${String.format("%.2f", speedup)}x faster than JSON5") + } else { + appendLine(" → JSON5 is ${String.format("%.2f", 1.0 / speedup)}x faster than JSON") + } + } + appendLine() + } + + // Overall statistics + appendLine("Overall Statistics:") + val json5Results = results.filter { it.format == "JSON5" } + val jsonResults = results.filter { it.format == "JSON" } + + val avgJson5Time = json5Results.map { it.averageTimeMillis }.average() + val avgJsonTime = jsonResults.map { it.averageTimeMillis }.average() + + appendLine("Average JSON5 time: ${String.format("%.3f", avgJson5Time)} ms") + appendLine("Average JSON time: ${String.format("%.3f", avgJsonTime)} ms") + + val overallSpeedup = avgJson5Time / avgJsonTime + if (overallSpeedup > 1.0) { + appendLine("Overall: JSON is ${String.format("%.2f", overallSpeedup)}x faster than JSON5") + } else { + appendLine("Overall: JSON5 is ${String.format("%.2f", 1.0 / overallSpeedup)}x faster than JSON") + } + } + + File(filename).writeText(report) + println("Summary report generated: $filename") + + // Also print summary to console + println() + println("=== BENCHMARK SUMMARY ===") + results.groupBy { "${it.dataType}_${it.operation}" }.forEach { (key, resultList) -> + val (dataType, operation) = key.split("_") + val json5Result = resultList.find { it.format == "JSON5" } + val jsonResult = resultList.find { it.format == "JSON" } + + if (json5Result != null && jsonResult != null) { + println("$dataType $operation: JSON5=${String.format("%.3f", json5Result.averageTimeMillis)}ms, JSON=${String.format("%.3f", jsonResult.averageTimeMillis)}ms") + } + } + } +} \ No newline at end of file diff --git a/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/SerializationBenchmark.kt b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/SerializationBenchmark.kt new file mode 100644 index 0000000..9e325d0 --- /dev/null +++ b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/SerializationBenchmark.kt @@ -0,0 +1,173 @@ +package dev.hossain.json5kt.benchmark + +import dev.hossain.json5kt.JSON5 +import kotlinx.serialization.json.Json +import kotlinx.serialization.encodeToString +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.serializer +import kotlin.system.measureNanoTime + +/** + * Benchmark class for comparing JSON5 and standard JSON performance. + */ +class SerializationBenchmark { + + private val json = Json { + prettyPrint = false + encodeDefaults = true + } + + data class BenchmarkResult( + val operation: String, + val dataType: String, + val format: String, + val iterations: Int, + val totalTimeNanos: Long, + val averageTimeNanos: Long, + val averageTimeMicros: Double, + val averageTimeMillis: Double + ) { + fun toCsvRow(): String { + return "$operation,$dataType,$format,$iterations,$totalTimeNanos,$averageTimeNanos,$averageTimeMicros,$averageTimeMillis" + } + + companion object { + fun csvHeader(): String { + return "Operation,DataType,Format,Iterations,TotalTimeNanos,AverageTimeNanos,AverageTimeMicros,AverageTimeMillis" + } + } + } + + /** + * Runs serialization benchmark for the given data and serializer. + */ + fun benchmarkSerialization( + data: T, + dataTypeName: String, + iterations: Int = 1000, + serializer: kotlinx.serialization.SerializationStrategy + ): List { + val results = mutableListOf() + + // Benchmark JSON5 serialization + val json5SerializationTime = measureNanoTime { + repeat(iterations) { + JSON5.encodeToString(serializer, data) + } + } + + results.add( + BenchmarkResult( + operation = "Serialization", + dataType = dataTypeName, + format = "JSON5", + iterations = iterations, + totalTimeNanos = json5SerializationTime, + averageTimeNanos = json5SerializationTime / iterations, + averageTimeMicros = (json5SerializationTime / iterations) / 1000.0, + averageTimeMillis = (json5SerializationTime / iterations) / 1_000_000.0 + ) + ) + + // Benchmark standard JSON serialization + val jsonSerializationTime = measureNanoTime { + repeat(iterations) { + json.encodeToString(serializer, data) + } + } + + results.add( + BenchmarkResult( + operation = "Serialization", + dataType = dataTypeName, + format = "JSON", + iterations = iterations, + totalTimeNanos = jsonSerializationTime, + averageTimeNanos = jsonSerializationTime / iterations, + averageTimeMicros = (jsonSerializationTime / iterations) / 1000.0, + averageTimeMillis = (jsonSerializationTime / iterations) / 1_000_000.0 + ) + ) + + return results + } + + /** + * Runs deserialization benchmark for the given JSON string and type. + */ + fun benchmarkDeserialization( + jsonString: String, + json5String: String, + dataTypeName: String, + iterations: Int = 1000, + deserializer: kotlinx.serialization.DeserializationStrategy + ): List { + val results = mutableListOf() + + // Benchmark JSON5 deserialization + val json5DeserializationTime = measureNanoTime { + repeat(iterations) { + JSON5.decodeFromString(deserializer, json5String) + } + } + + results.add( + BenchmarkResult( + operation = "Deserialization", + dataType = dataTypeName, + format = "JSON5", + iterations = iterations, + totalTimeNanos = json5DeserializationTime, + averageTimeNanos = json5DeserializationTime / iterations, + averageTimeMicros = (json5DeserializationTime / iterations) / 1000.0, + averageTimeMillis = (json5DeserializationTime / iterations) / 1_000_000.0 + ) + ) + + // Benchmark standard JSON deserialization + val jsonDeserializationTime = measureNanoTime { + repeat(iterations) { + json.decodeFromString(deserializer, jsonString) + } + } + + results.add( + BenchmarkResult( + operation = "Deserialization", + dataType = dataTypeName, + format = "JSON", + iterations = iterations, + totalTimeNanos = jsonDeserializationTime, + averageTimeNanos = jsonDeserializationTime / iterations, + averageTimeMicros = (jsonDeserializationTime / iterations) / 1000.0, + averageTimeMillis = (jsonDeserializationTime / iterations) / 1_000_000.0 + ) + ) + + return results + } + + /** + * Runs complete benchmark (both serialization and deserialization) for given data. + */ + fun benchmarkComplete( + data: T, + dataTypeName: String, + iterations: Int = 1000, + serializer: kotlinx.serialization.KSerializer + ): List { + val results = mutableListOf() + + // First serialize to get strings for deserialization benchmark + val json5String = JSON5.encodeToString(serializer, data) + val jsonString = json.encodeToString(serializer, data) + + // Run serialization benchmarks + results.addAll(benchmarkSerialization(data, dataTypeName, iterations, serializer)) + + // Run deserialization benchmarks + results.addAll(benchmarkDeserialization(jsonString, json5String, dataTypeName, iterations, serializer)) + + return results + } +} \ No newline at end of file diff --git a/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/TestData.kt b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/TestData.kt new file mode 100644 index 0000000..714a74b --- /dev/null +++ b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/TestData.kt @@ -0,0 +1,76 @@ +package dev.hossain.json5kt.benchmark + +import kotlinx.serialization.Serializable + +/** + * Data models used for benchmarking serialization/deserialization performance. + */ + +@Serializable +data class SimplePerson( + val name: String, + val age: Int, + val isActive: Boolean = true +) + +@Serializable +data class ComplexPerson( + val firstName: String, + val lastName: String, + val age: Int, + val email: String, + val isActive: Boolean, + val salary: Double, + val address: Address, + val phoneNumbers: List, + val skills: List, + val metadata: Map +) + +@Serializable +data class Address( + val street: String, + val city: String, + val state: String, + val zipCode: String, + val country: String +) + +@Serializable +data class Company( + val name: String, + val employees: List, + val departments: List, + val founded: Int, + val revenue: Long, + val isPublic: Boolean +) + +@Serializable +data class Department( + val name: String, + val manager: SimplePerson, + val budget: Double, + val projects: List +) + +@Serializable +data class NumberTypes( + val intValue: Int, + val longValue: Long, + val doubleValue: Double, + val floatValue: Float, + val byteValue: Byte, + val shortValue: Short +) + +@Serializable +data class CollectionTypes( + val stringList: List, + val intList: List, + val booleanList: List, + val nestedList: List>, + val stringMap: Map, + val intMap: Map, + val nestedMap: Map> +) \ No newline at end of file diff --git a/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/TestDataGenerator.kt b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/TestDataGenerator.kt new file mode 100644 index 0000000..968eaad --- /dev/null +++ b/benchmark/src/main/kotlin/dev/hossain/json5kt/benchmark/TestDataGenerator.kt @@ -0,0 +1,170 @@ +package dev.hossain.json5kt.benchmark + +/** + * Generates test data for benchmarking. + */ +object TestDataGenerator { + + fun createSimplePerson(): SimplePerson { + return SimplePerson( + name = "John Doe", + age = 30, + isActive = true + ) + } + + fun createComplexPerson(): ComplexPerson { + return ComplexPerson( + firstName = "Jane", + lastName = "Smith", + age = 28, + email = "jane.smith@example.com", + isActive = true, + salary = 75000.50, + address = Address( + street = "123 Main St", + city = "San Francisco", + state = "CA", + zipCode = "94102", + country = "USA" + ), + phoneNumbers = listOf("+1-555-123-4567", "+1-555-987-6543"), + skills = listOf("Kotlin", "Java", "Python", "JavaScript", "SQL"), + metadata = mapOf( + "department" to "Engineering", + "level" to "Senior", + "team" to "Backend" + ) + ) + } + + fun createCompany(): Company { + val employees = (1..10).map { i -> + ComplexPerson( + firstName = "Employee$i", + lastName = "Last$i", + age = 25 + i, + email = "employee$i@company.com", + isActive = i % 2 == 0, + salary = 50000.0 + (i * 5000), + address = Address( + street = "$i Main St", + city = "City$i", + state = "ST", + zipCode = "1000$i", + country = "USA" + ), + phoneNumbers = listOf("+1-555-000-000$i"), + skills = listOf("Skill$i", "Skill${i+1}"), + metadata = mapOf("id" to i.toString()) + ) + } + + val departments = listOf( + Department( + name = "Engineering", + manager = createSimplePerson(), + budget = 1000000.0, + projects = listOf("Project A", "Project B", "Project C") + ), + Department( + name = "Marketing", + manager = SimplePerson("Mary Johnson", 35, true), + budget = 500000.0, + projects = listOf("Campaign X", "Campaign Y") + ), + Department( + name = "Sales", + manager = SimplePerson("Bob Wilson", 40, true), + budget = 750000.0, + projects = listOf("Q1 Sales", "Q2 Sales", "Q3 Sales", "Q4 Sales") + ) + ) + + return Company( + name = "TechCorp Inc.", + employees = employees, + departments = departments, + founded = 2010, + revenue = 50000000L, + isPublic = true + ) + } + + fun createNumberTypes(): NumberTypes { + return NumberTypes( + intValue = 42, + longValue = 1234567890123L, + doubleValue = 3.14159265359, + floatValue = 2.71828f, + byteValue = 127, + shortValue = 32767 + ) + } + + fun createCollectionTypes(): CollectionTypes { + return CollectionTypes( + stringList = listOf("apple", "banana", "cherry", "date", "elderberry"), + intList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + booleanList = listOf(true, false, true, false, true), + nestedList = listOf( + listOf("a", "b", "c"), + listOf("d", "e", "f"), + listOf("g", "h", "i") + ), + stringMap = mapOf( + "key1" to "value1", + "key2" to "value2", + "key3" to "value3" + ), + intMap = mapOf( + "one" to 1, + "two" to 2, + "three" to 3 + ), + nestedMap = mapOf( + "config" to mapOf("debug" to "true", "version" to "1.0"), + "settings" to mapOf("theme" to "dark", "lang" to "en") + ) + ) + } + + /** + * Creates a list of simple persons for bulk testing. + */ + fun createSimplePersonList(count: Int): List { + return (1..count).map { i -> + SimplePerson( + name = "Person $i", + age = 20 + (i % 50), + isActive = i % 2 == 0 + ) + } + } + + /** + * Creates a list of complex persons for bulk testing. + */ + fun createComplexPersonList(count: Int): List { + return (1..count).map { i -> + ComplexPerson( + firstName = "First$i", + lastName = "Last$i", + age = 20 + (i % 50), + email = "person$i@example.com", + isActive = i % 3 != 0, + salary = 40000.0 + (i * 1000), + address = Address( + street = "$i Street", + city = "City ${i % 10}", + state = "ST", + zipCode = "${10000 + i}", + country = "Country ${i % 5}" + ), + phoneNumbers = listOf("+1-555-${String.format("%03d", i % 1000)}-${String.format("%04d", i)}"), + skills = listOf("Skill${i % 10}", "Skill${(i + 1) % 10}"), + metadata = mapOf("id" to i.toString(), "batch" to (i / 100).toString()) + ) + } + } +} \ No newline at end of file diff --git a/benchmark/src/test/kotlin/dev/hossain/json5kt/benchmark/BenchmarkTest.kt b/benchmark/src/test/kotlin/dev/hossain/json5kt/benchmark/BenchmarkTest.kt new file mode 100644 index 0000000..fc10343 --- /dev/null +++ b/benchmark/src/test/kotlin/dev/hossain/json5kt/benchmark/BenchmarkTest.kt @@ -0,0 +1,105 @@ +package dev.hossain.json5kt.benchmark + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.* + +/** + * Tests for the benchmark module to ensure it works correctly. + */ +class BenchmarkTest { + + @Test + fun `test data generation works`() { + val simplePerson = TestDataGenerator.createSimplePerson() + assertNotNull(simplePerson) + assertEquals("John Doe", simplePerson.name) + assertEquals(30, simplePerson.age) + assertTrue(simplePerson.isActive) + + val complexPerson = TestDataGenerator.createComplexPerson() + assertNotNull(complexPerson) + assertEquals("Jane", complexPerson.firstName) + assertNotNull(complexPerson.address) + assertTrue(complexPerson.phoneNumbers.isNotEmpty()) + + val company = TestDataGenerator.createCompany() + assertNotNull(company) + assertEquals("TechCorp Inc.", company.name) + assertEquals(10, company.employees.size) + assertEquals(3, company.departments.size) + } + + @Test + fun `test benchmark can run without errors`() { + val benchmark = SerializationBenchmark() + val testData = TestDataGenerator.createSimplePerson() + + // Run a small benchmark to ensure no errors + val results = benchmark.benchmarkComplete( + testData, + "TestPerson", + 10, // Small number of iterations for test + SimplePerson.serializer() + ) + + assertNotNull(results) + assertTrue(results.isNotEmpty()) + + // Should have 4 results: JSON5 and JSON for both serialization and deserialization + assertEquals(4, results.size) + + val json5SerializationResult = results.find { it.format == "JSON5" && it.operation == "Serialization" } + val jsonSerializationResult = results.find { it.format == "JSON" && it.operation == "Serialization" } + val json5DeserializationResult = results.find { it.format == "JSON5" && it.operation == "Deserialization" } + val jsonDeserializationResult = results.find { it.format == "JSON" && it.operation == "Deserialization" } + + assertNotNull(json5SerializationResult) + assertNotNull(jsonSerializationResult) + assertNotNull(json5DeserializationResult) + assertNotNull(jsonDeserializationResult) + + // Verify basic properties + assertEquals("TestPerson", json5SerializationResult!!.dataType) + assertEquals(10, json5SerializationResult.iterations) + assertTrue(json5SerializationResult.totalTimeNanos > 0) + assertTrue(json5SerializationResult.averageTimeNanos > 0) + } + + @Test + fun `test benchmark result CSV generation`() { + val result = SerializationBenchmark.BenchmarkResult( + operation = "Serialization", + dataType = "TestType", + format = "JSON5", + iterations = 100, + totalTimeNanos = 1000000, + averageTimeNanos = 10000, + averageTimeMicros = 10.0, + averageTimeMillis = 0.01 + ) + + val csvRow = result.toCsvRow() + assertEquals("Serialization,TestType,JSON5,100,1000000,10000,10.0,0.01", csvRow) + + val csvHeader = SerializationBenchmark.BenchmarkResult.csvHeader() + assertEquals("Operation,DataType,Format,Iterations,TotalTimeNanos,AverageTimeNanos,AverageTimeMicros,AverageTimeMillis", csvHeader) + } + + @Test + fun `test collection data generators`() { + val simplePersonList = TestDataGenerator.createSimplePersonList(5) + assertEquals(5, simplePersonList.size) + + val complexPersonList = TestDataGenerator.createComplexPersonList(3) + assertEquals(3, complexPersonList.size) + + val numberTypes = TestDataGenerator.createNumberTypes() + assertNotNull(numberTypes) + assertEquals(42, numberTypes.intValue) + + val collectionTypes = TestDataGenerator.createCollectionTypes() + assertNotNull(collectionTypes) + assertTrue(collectionTypes.stringList.isNotEmpty()) + assertTrue(collectionTypes.stringMap.isNotEmpty()) + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3c8d8d5..3c39ca9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,10 +16,11 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } -// Include the `app`, `lib` and `utils` subprojects in the build. +// Include the `app`, `lib` and `benchmark` subprojects in the build. // If there are changes in only one of the projects, Gradle will rebuild only the one that has changed. // Learn more about structuring projects with Gradle - https://docs.gradle.org/8.7/userguide/multi_project_builds.html include(":app") include(":lib") +include(":benchmark") rootProject.name = "json5-kotlin" \ No newline at end of file