685 lines
15 KiB
Go
685 lines
15 KiB
Go
package scf_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
config "git.sharkk.net/Go/SCF"
|
|
"github.com/BurntSushi/toml"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func BenchmarkRetrieveSimpleValues(b *testing.B) {
|
|
configData := `
|
|
host "localhost"
|
|
port 8080
|
|
debug true
|
|
timeout 30
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, err := cfg.GetString("host")
|
|
if err != nil || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host: %v", err)
|
|
}
|
|
|
|
// Int retrieval
|
|
port, err := cfg.GetInt("port")
|
|
if err != nil || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port: %v", err)
|
|
}
|
|
|
|
// Bool retrieval
|
|
debug, err := cfg.GetBool("debug")
|
|
if err != nil || !debug {
|
|
b.Fatalf("Failed to retrieve debug: %v", err)
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout, err := cfg.Get("timeout")
|
|
if err != nil || timeout.(int) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveNestedValues(b *testing.B) {
|
|
configData := `
|
|
app {
|
|
name "TestApp"
|
|
version "1.0.0"
|
|
settings {
|
|
enableLogging true
|
|
maxConnections 100
|
|
}
|
|
}
|
|
database {
|
|
host "db.example.com"
|
|
port 5432
|
|
credentials {
|
|
username "admin"
|
|
password "secure123"
|
|
}
|
|
}
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// First level nesting
|
|
appName, err := cfg.GetString("app.name")
|
|
if err != nil || appName != "TestApp" {
|
|
b.Fatalf("Failed to retrieve app.name: %v", err)
|
|
}
|
|
|
|
dbPort, err := cfg.GetInt("database.port")
|
|
if err != nil || dbPort != 5432 {
|
|
b.Fatalf("Failed to retrieve database.port: %v", err)
|
|
}
|
|
|
|
// Second level nesting
|
|
logging, err := cfg.GetBool("app.settings.enableLogging")
|
|
if err != nil || !logging {
|
|
b.Fatalf("Failed to retrieve app.settings.enableLogging: %v", err)
|
|
}
|
|
|
|
username, err := cfg.GetString("database.credentials.username")
|
|
if err != nil || username != "admin" {
|
|
b.Fatalf("Failed to retrieve database.credentials.username: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveArrayValues(b *testing.B) {
|
|
configData := `
|
|
features {
|
|
"authentication"
|
|
"authorization"
|
|
"reporting"
|
|
"analytics"
|
|
}
|
|
numbers {
|
|
1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
}
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Get entire array
|
|
features, err := cfg.GetArray("features")
|
|
if err != nil || len(features) != 4 {
|
|
b.Fatalf("Failed to retrieve features array: %v", err)
|
|
}
|
|
|
|
// Check specific array element
|
|
if feature0, ok := features[0].(string); !ok || feature0 != "authentication" {
|
|
b.Fatalf("Failed to access array element: %v", features[0])
|
|
}
|
|
|
|
// Get with dot notation for array access
|
|
auth, err := cfg.GetString("features.0")
|
|
if err != nil || auth != "authentication" {
|
|
b.Fatalf("Failed to retrieve features.0: %v", err)
|
|
}
|
|
|
|
// Numeric array
|
|
numbers, err := cfg.GetArray("numbers")
|
|
if err != nil || len(numbers) != 5 {
|
|
b.Fatalf("Failed to retrieve numbers array: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveMixedValues(b *testing.B) {
|
|
configData := `
|
|
app {
|
|
name "TestApp"
|
|
version "1.0.0"
|
|
environments {
|
|
"development"
|
|
"testing"
|
|
"production"
|
|
}
|
|
limits {
|
|
requests 1000
|
|
connections 100
|
|
timeouts {
|
|
read 5
|
|
write 10
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
reader := strings.NewReader(configData)
|
|
cfg, err := config.Load(reader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse config: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Simple retrieval
|
|
appName, err := cfg.GetString("app.name")
|
|
if err != nil || appName != "TestApp" {
|
|
b.Fatalf("Failed to retrieve app.name: %v", err)
|
|
}
|
|
|
|
// Array retrieval
|
|
environments, err := cfg.GetArray("app.environments")
|
|
if err != nil || len(environments) != 3 {
|
|
b.Fatalf("Failed to retrieve app.environments: %v", err)
|
|
}
|
|
|
|
// Deeply nested value
|
|
readTimeout, err := cfg.GetInt("app.limits.timeouts.read")
|
|
if err != nil || readTimeout != 5 {
|
|
b.Fatalf("Failed to retrieve app.limits.timeouts.read: %v", err)
|
|
}
|
|
|
|
// Array item with dot notation
|
|
prod, err := cfg.GetString("app.environments.2")
|
|
if err != nil || prod != "production" {
|
|
b.Fatalf("Failed to retrieve app.environments.2: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for JSON
|
|
func BenchmarkRetrieveSimpleValuesJSON(b *testing.B) {
|
|
configData := `{
|
|
"host": "localhost",
|
|
"port": 8080,
|
|
"debug": true,
|
|
"timeout": 30
|
|
}`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]any
|
|
err := json.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, ok := result["host"].(string)
|
|
if !ok || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host")
|
|
}
|
|
|
|
// Number retrieval
|
|
port, ok := result["port"].(float64)
|
|
if !ok || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port")
|
|
}
|
|
|
|
// Boolean retrieval
|
|
debug, ok := result["debug"].(bool)
|
|
if !ok || !debug {
|
|
b.Fatalf("Failed to retrieve debug")
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout := result["timeout"]
|
|
if timeout.(float64) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveNestedValuesJSON(b *testing.B) {
|
|
configData := `{
|
|
"app": {
|
|
"name": "TestApp",
|
|
"version": "1.0.0",
|
|
"settings": {
|
|
"enableLogging": true,
|
|
"maxConnections": 100
|
|
}
|
|
},
|
|
"database": {
|
|
"host": "db.example.com",
|
|
"port": 5432,
|
|
"credentials": {
|
|
"username": "admin",
|
|
"password": "secure123"
|
|
}
|
|
}
|
|
}`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]any
|
|
err := json.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// First level nesting
|
|
app, ok := result["app"].(map[string]any)
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve app")
|
|
}
|
|
appName, ok := app["name"].(string)
|
|
if !ok || appName != "TestApp" {
|
|
b.Fatalf("Failed to retrieve app.name")
|
|
}
|
|
|
|
database, ok := result["database"].(map[string]any)
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve database")
|
|
}
|
|
dbPort, ok := database["port"].(float64)
|
|
if !ok || dbPort != 5432 {
|
|
b.Fatalf("Failed to retrieve database.port")
|
|
}
|
|
|
|
// Second level nesting
|
|
settings, ok := app["settings"].(map[string]any)
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve app.settings")
|
|
}
|
|
logging, ok := settings["enableLogging"].(bool)
|
|
if !ok || !logging {
|
|
b.Fatalf("Failed to retrieve app.settings.enableLogging")
|
|
}
|
|
|
|
credentials, ok := database["credentials"].(map[string]any)
|
|
if !ok {
|
|
b.Fatalf("Failed to retrieve database.credentials")
|
|
}
|
|
username, ok := credentials["username"].(string)
|
|
if !ok || username != "admin" {
|
|
b.Fatalf("Failed to retrieve database.credentials.username")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkRetrieveArrayValuesJSON(b *testing.B) {
|
|
configData := `{
|
|
"features": [
|
|
"authentication",
|
|
"authorization",
|
|
"reporting",
|
|
"analytics"
|
|
],
|
|
"numbers": [1, 2, 3, 4, 5]
|
|
}`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]any
|
|
err := json.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Get entire array
|
|
features, ok := result["features"].([]any)
|
|
if !ok || len(features) != 4 {
|
|
b.Fatalf("Failed to retrieve features array")
|
|
}
|
|
|
|
// Check specific array element
|
|
if feature0, ok := features[0].(string); !ok || feature0 != "authentication" {
|
|
b.Fatalf("Failed to access array element")
|
|
}
|
|
|
|
// Numeric array
|
|
numbers, ok := result["numbers"].([]any)
|
|
if !ok || len(numbers) != 5 {
|
|
b.Fatalf("Failed to retrieve numbers array")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for YAML
|
|
func BenchmarkRetrieveSimpleValuesYAML(b *testing.B) {
|
|
configData := `
|
|
host: localhost
|
|
port: 8080
|
|
debug: true
|
|
timeout: 30
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]any
|
|
err := yaml.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse YAML: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, ok := result["host"].(string)
|
|
if !ok || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host")
|
|
}
|
|
|
|
// Number retrieval
|
|
port, ok := result["port"].(int)
|
|
if !ok || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port")
|
|
}
|
|
|
|
// Boolean retrieval
|
|
debug, ok := result["debug"].(bool)
|
|
if !ok || !debug {
|
|
b.Fatalf("Failed to retrieve debug")
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout := result["timeout"]
|
|
if timeout.(int) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Retrieval Benchmarks for TOML
|
|
func BenchmarkRetrieveSimpleValuesTOML(b *testing.B) {
|
|
configData := `
|
|
host = "localhost"
|
|
port = 8080
|
|
debug = true
|
|
timeout = 30
|
|
`
|
|
|
|
// Parse once before benchmarking retrieval
|
|
var result map[string]any
|
|
err := toml.Unmarshal([]byte(configData), &result)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse TOML: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// String retrieval
|
|
host, ok := result["host"].(string)
|
|
if !ok || host != "localhost" {
|
|
b.Fatalf("Failed to retrieve host")
|
|
}
|
|
|
|
// Number retrieval
|
|
port, ok := result["port"].(int64)
|
|
if !ok || port != 8080 {
|
|
b.Fatalf("Failed to retrieve port")
|
|
}
|
|
|
|
// Boolean retrieval
|
|
debug, ok := result["debug"].(bool)
|
|
if !ok || !debug {
|
|
b.Fatalf("Failed to retrieve debug")
|
|
}
|
|
|
|
// Generic retrieval
|
|
timeout := result["timeout"]
|
|
if timeout.(int64) != 30 {
|
|
b.Fatalf("Failed to retrieve timeout")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Comprehensive value retrieval comparison benchmark
|
|
func BenchmarkComplexValueRetrieval(b *testing.B) {
|
|
// Setup complex config with deep nesting and arrays for all formats
|
|
customConfig := `
|
|
app {
|
|
name "TestApp"
|
|
version "1.0.0"
|
|
settings {
|
|
enableLogging true
|
|
maxConnections 100
|
|
timeouts {
|
|
read 5
|
|
write 10
|
|
}
|
|
}
|
|
environments {
|
|
"development"
|
|
"testing"
|
|
"production"
|
|
}
|
|
}
|
|
`
|
|
|
|
jsonConfig := `{
|
|
"app": {
|
|
"name": "TestApp",
|
|
"version": "1.0.0",
|
|
"settings": {
|
|
"enableLogging": true,
|
|
"maxConnections": 100,
|
|
"timeouts": {
|
|
"read": 5,
|
|
"write": 10
|
|
}
|
|
},
|
|
"environments": [
|
|
"development",
|
|
"testing",
|
|
"production"
|
|
]
|
|
}
|
|
}`
|
|
|
|
yamlConfig := `
|
|
app:
|
|
name: TestApp
|
|
version: 1.0.0
|
|
settings:
|
|
enableLogging: true
|
|
maxConnections: 100
|
|
timeouts:
|
|
read: 5
|
|
write: 10
|
|
environments:
|
|
- development
|
|
- testing
|
|
- production
|
|
`
|
|
|
|
tomlConfig := `
|
|
[app]
|
|
name = "TestApp"
|
|
version = "1.0.0"
|
|
environments = ["development", "testing", "production"]
|
|
|
|
[app.settings]
|
|
enableLogging = true
|
|
maxConnections = 100
|
|
|
|
[app.settings.timeouts]
|
|
read = 5
|
|
write = 10
|
|
`
|
|
|
|
// Parse configs once before benchmarking
|
|
|
|
// Custom config
|
|
customReader := strings.NewReader(customConfig)
|
|
customCfg, err := config.Load(customReader)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse custom config: %v", err)
|
|
}
|
|
|
|
// JSON config
|
|
var jsonResult map[string]any
|
|
err = json.Unmarshal([]byte(jsonConfig), &jsonResult)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse JSON: %v", err)
|
|
}
|
|
|
|
// YAML config
|
|
var yamlResult map[string]any
|
|
err = yaml.Unmarshal([]byte(yamlConfig), &yamlResult)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse YAML: %v", err)
|
|
}
|
|
|
|
// TOML config
|
|
var tomlResult map[string]any
|
|
err = toml.Unmarshal([]byte(tomlConfig), &tomlResult)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse TOML: %v", err)
|
|
}
|
|
|
|
directData := customCfg.GetData()
|
|
|
|
b.Run("Custom-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := customCfg.GetString("app.name")
|
|
if err != nil {
|
|
b.Fatalf("Failed to retrieve app.name: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("Custom-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := customCfg.GetInt("app.settings.timeouts.read")
|
|
if err != nil {
|
|
b.Fatalf("Failed to retrieve app.settings.timeouts.read: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("Custom-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := customCfg.GetString("app.environments.1")
|
|
if err != nil {
|
|
b.Fatalf("Failed to retrieve app.environments.1: %v", err)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("Direct-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := directData["app"].(map[string]any)
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("Direct-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := directData["app"].(map[string]any)
|
|
settings := app["settings"].(map[string]any)
|
|
timeouts := settings["timeouts"].(map[string]any)
|
|
_ = timeouts["read"].(int)
|
|
}
|
|
})
|
|
|
|
b.Run("Direct-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := directData["app"].(map[string]any)
|
|
environments := app["environments"].([]any)
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("JSON-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := jsonResult["app"].(map[string]any)
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("JSON-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := jsonResult["app"].(map[string]any)
|
|
settings := app["settings"].(map[string]any)
|
|
timeouts := settings["timeouts"].(map[string]any)
|
|
_ = timeouts["read"].(float64)
|
|
}
|
|
})
|
|
|
|
b.Run("JSON-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := jsonResult["app"].(map[string]any)
|
|
environments := app["environments"].([]any)
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("YAML-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := yamlResult["app"].(map[string]any)
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("YAML-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := yamlResult["app"].(map[string]any)
|
|
settings := app["settings"].(map[string]any)
|
|
timeouts := settings["timeouts"].(map[string]any)
|
|
_ = timeouts["read"].(int)
|
|
}
|
|
})
|
|
|
|
b.Run("YAML-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := yamlResult["app"].(map[string]any)
|
|
environments := app["environments"].([]any)
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("TOML-SimpleGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := tomlResult["app"].(map[string]any)
|
|
_ = app["name"].(string)
|
|
}
|
|
})
|
|
|
|
b.Run("TOML-DeepGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := tomlResult["app"].(map[string]any)
|
|
settings := app["settings"].(map[string]any)
|
|
timeouts := settings["timeouts"].(map[string]any)
|
|
_ = timeouts["read"].(int64)
|
|
}
|
|
})
|
|
|
|
b.Run("TOML-ArrayGet", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
app := tomlResult["app"].(map[string]any)
|
|
environments := app["environments"].([]any)
|
|
_ = environments[1].(string)
|
|
}
|
|
})
|
|
}
|