Files
toml/decode_fsm_test.go
T
2026-07-13 02:45:49 -04:00

844 lines
20 KiB
Go

package toml
import (
"testing"
)
// TestDecode_MapPointerValues tests map[string]*Struct decoding
// This is the exact pattern used by RootConfig.States
func TestDecode_MapPointerValues(t *testing.T) {
data := map[string]any{
"items": map[string]any{
"first": map[string]any{
"name": "alpha",
},
"second": map[string]any{
"name": "beta",
},
},
}
type Item struct {
Name string `toml:"name"`
}
type Config struct {
Items map[string]*Item `toml:"items"`
}
var cfg Config
if err := Decode(data, &cfg); err != nil {
t.Fatalf("Decode failed: %v", err)
}
if cfg.Items == nil {
t.Fatal("Items map is nil")
}
if len(cfg.Items) != 2 {
t.Fatalf("Expected 2 items, got %d", len(cfg.Items))
}
if cfg.Items["first"] == nil || cfg.Items["first"].Name != "alpha" {
t.Errorf("first item mismatch: %+v", cfg.Items["first"])
}
if cfg.Items["second"] == nil || cfg.Items["second"].Name != "beta" {
t.Errorf("second item mismatch: %+v", cfg.Items["second"])
}
}
// TestUnmarshal_DottedTableToMapPointer tests [parent.child] -> map[string]*Struct
func TestUnmarshal_DottedTableToMapPointer(t *testing.T) {
input := []byte(`
[states.Gameplay]
parent = "Root"
[states.TrySpawn]
parent = "Gameplay"
`)
type StateConfig struct {
Parent string `toml:"parent"`
}
type Config struct {
States map[string]*StateConfig `toml:"states"`
}
var cfg Config
if err := Unmarshal(input, &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
if cfg.States == nil {
t.Fatal("States map is nil")
}
if len(cfg.States) != 2 {
t.Fatalf("Expected 2 states, got %d", len(cfg.States))
}
if cfg.States["Gameplay"] == nil {
t.Fatal("Gameplay state is nil")
}
if cfg.States["Gameplay"].Parent != "Root" {
t.Errorf("Gameplay.Parent mismatch: %q", cfg.States["Gameplay"].Parent)
}
}
// TestUnmarshal_InlineTableArray tests arrays of inline tables
func TestUnmarshal_InlineTableArray(t *testing.T) {
input := []byte(`
[state]
transitions = [
{ trigger = "EventA", target = "StateB" },
{ trigger = "EventB", target = "StateC", guard = "CheckX" }
]
`)
type Transition struct {
Trigger string `toml:"trigger"`
Target string `toml:"target"`
Guard string `toml:"guard,omitempty"`
}
type State struct {
Transitions []Transition `toml:"transitions"`
}
type Config struct {
State State `toml:"state"`
}
var cfg Config
if err := Unmarshal(input, &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
if len(cfg.State.Transitions) != 2 {
t.Fatalf("Expected 2 transitions, got %d", len(cfg.State.Transitions))
}
if cfg.State.Transitions[0].Trigger != "EventA" {
t.Errorf("Transition[0].Trigger mismatch: %q", cfg.State.Transitions[0].Trigger)
}
if cfg.State.Transitions[1].Guard != "CheckX" {
t.Errorf("Transition[1].Guard mismatch: %q", cfg.State.Transitions[1].Guard)
}
}
func TestUnmarshal_MultilineInlineTable(t *testing.T) {
input := []byte(`
[state]
config = {
name = "test",
nested = { a = 1, b = 2 },
array = [
{ x = 10 },
{ x = 20 }
]
}
`)
type Inner struct {
X int `toml:"x"`
}
type Config struct {
Name string `toml:"name"`
Nested map[string]int `toml:"nested"`
Array []Inner `toml:"array"`
}
type State struct {
Config Config `toml:"config"`
}
type Root struct {
State State `toml:"state"`
}
var cfg Root
if err := Unmarshal(input, &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
if cfg.State.Config.Name != "test" {
t.Errorf("Name = %q", cfg.State.Config.Name)
}
if cfg.State.Config.Nested["a"] != 1 {
t.Errorf("Nested.a = %d", cfg.State.Config.Nested["a"])
}
if len(cfg.State.Config.Array) != 2 || cfg.State.Config.Array[1].X != 20 {
t.Errorf("Array = %+v", cfg.State.Config.Array)
}
}
func TestUnmarshal_DeeplyNestedMultiline(t *testing.T) {
input := []byte(`
transition = { trigger = "Tick", target = "Active", guard = "Or", guard_args = { guards = [
{ name = "Compare", args = { key = "val", op = "gt", value = 0 } },
{ name = "Check", args = { flag = true } }
]} }
`)
type Args struct {
Key string `toml:"key"`
Op string `toml:"op"`
Value int `toml:"value"`
Flag bool `toml:"flag"`
}
type Guard struct {
Name string `toml:"name"`
Args Args `toml:"args"`
}
type GuardArgs struct {
Guards []Guard `toml:"guards"`
}
type Transition struct {
Trigger string `toml:"trigger"`
Target string `toml:"target"`
Guard string `toml:"guard"`
GuardArgs GuardArgs `toml:"guard_args"`
}
type Root struct {
Transition Transition `toml:"transition"`
}
var cfg Root
if err := Unmarshal(input, &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
if cfg.Transition.Guard != "Or" {
t.Errorf("Guard = %q", cfg.Transition.Guard)
}
if len(cfg.Transition.GuardArgs.Guards) != 2 {
t.Fatalf("Guards count = %d", len(cfg.Transition.GuardArgs.Guards))
}
if cfg.Transition.GuardArgs.Guards[0].Args.Op != "gt" {
t.Errorf("Guards[0].Args.Op = %q", cfg.Transition.GuardArgs.Guards[0].Args.Op)
}
}
// TestUnmarshal_FSMConfigExact tests the exact FSM config structure
func TestUnmarshal_FSMConfigExact(t *testing.T) {
input := []byte(`
initial = "TrySpawnGold"
[states.Gameplay]
parent = "Root"
[states.TrySpawnGold]
parent = "Gameplay"
on_enter = [
{ action = "EmitEvent", event = "EventGoldSpawnRequest" }
]
transitions = [
{ trigger = "EventGoldSpawned", target = "GoldActive" },
{ trigger = "EventGoldSpawnFailed", target = "GoldRetryWait" }
]
[states.GoldRetryWait]
parent = "Gameplay"
transitions = [
{ trigger = "Tick", target = "TrySpawnGold", guard = "StateTimeExceeds", guard_args = { ms = 2000 } }
]
[states.GoldActive]
parent = "Gameplay"
transitions = [
{ trigger = "EventGoldCollected", target = "TrySpawnGold" }
]
`)
type ActionConfig struct {
Action string `toml:"action"`
Event string `toml:"event,omitempty"`
}
type TransitionConfig struct {
Trigger string `toml:"trigger"`
Target string `toml:"target"`
Guard string `toml:"guard,omitempty"`
GuardArgs map[string]any `toml:"guard_args,omitempty"`
}
type StateConfig struct {
Parent string `toml:"parent,omitempty"`
OnEnter []ActionConfig `toml:"on_enter,omitempty"`
Transitions []TransitionConfig `toml:"transitions,omitempty"`
}
type RootConfig struct {
InitialState string `toml:"initial"`
States map[string]*StateConfig `toml:"states"`
}
var cfg RootConfig
if err := Unmarshal(input, &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
// Check initial
if cfg.InitialState != "TrySpawnGold" {
t.Errorf("InitialState mismatch: %q", cfg.InitialState)
}
// Check states map
if cfg.States == nil {
t.Fatal("States map is nil")
}
if len(cfg.States) != 4 {
t.Errorf("Expected 4 states, got %d", len(cfg.States))
for k := range cfg.States {
t.Logf(" Found state: %q", k)
}
}
// Check TrySpawnGold
tsg := cfg.States["TrySpawnGold"]
if tsg == nil {
t.Fatal("TrySpawnGold state is nil")
}
if tsg.Parent != "Gameplay" {
t.Errorf("TrySpawnGold.Parent mismatch: %q", tsg.Parent)
}
if len(tsg.OnEnter) != 1 {
t.Errorf("TrySpawnGold.OnEnter count mismatch: %d", len(tsg.OnEnter))
}
if len(tsg.Transitions) != 2 {
t.Errorf("TrySpawnGold.Transitions count mismatch: %d", len(tsg.Transitions))
}
// Check GoldRetryWait guard_args
grw := cfg.States["GoldRetryWait"]
if grw == nil {
t.Fatal("GoldRetryWait state is nil")
}
if len(grw.Transitions) != 1 {
t.Fatalf("GoldRetryWait.Transitions count mismatch: %d", len(grw.Transitions))
}
if grw.Transitions[0].GuardArgs == nil {
t.Error("GuardArgs is nil")
} else if ms, ok := grw.Transitions[0].GuardArgs["ms"]; !ok {
t.Error("GuardArgs missing 'ms' key")
} else if msInt, ok := ms.(int); !ok || msInt != 2000 {
t.Errorf("GuardArgs.ms mismatch: %T %v", ms, ms)
}
}
// TestParser_DottedTableStructure verifies parser output for dotted tables
func TestParser_DottedTableStructure(t *testing.T) {
input := []byte(`
[states.Alpha]
name = "first"
[states.Beta]
name = "second"
`)
p := NewParser(input)
result, err := p.Parse()
if err != nil {
t.Fatalf("Parse failed: %v", err)
}
// Check raw parser output structure
states, ok := result["states"]
if !ok {
t.Fatal("'states' key missing from parser output")
}
statesMap, ok := states.(map[string]any)
if !ok {
t.Fatalf("'states' is not map[string]any, got %T", states)
}
if len(statesMap) != 2 {
t.Errorf("Expected 2 states in parser output, got %d", len(statesMap))
}
alpha, ok := statesMap["Alpha"]
if !ok {
t.Error("'Alpha' key missing")
}
alphaMap, ok := alpha.(map[string]any)
if !ok {
t.Fatalf("'Alpha' is not map[string]any, got %T", alpha)
}
if alphaMap["name"] != "first" {
t.Errorf("Alpha.name mismatch: %v", alphaMap["name"])
}
}
// TestDecode_MapNilInitialization verifies map initialization during decode
func TestDecode_MapNilInitialization(t *testing.T) {
data := map[string]any{
"items": map[string]any{
"a": map[string]any{"val": 1},
},
}
type Item struct {
Val int `toml:"val"`
}
type Config struct {
Items map[string]*Item `toml:"items"` // nil initially
}
var cfg Config
// cfg.Items is nil here
if err := Decode(data, &cfg); err != nil {
t.Fatalf("Decode failed: %v", err)
}
if cfg.Items == nil {
t.Fatal("Decode did not initialize nil map")
}
}
func TestUnmarshal_ExtremeComplexity(t *testing.T) {
input := []byte(`
# Root level mixed types
version = "2.0.0-beta"
debug = true
tick_rate = 144
delta_time = 0.00694
# Deep dotted header (5 levels)
[engine.renderer.pipeline.stage.config]
name = "deferred"
priority = 1
enabled = true
scale_factor = 1.5e-2
tags = ["lighting", "shadows", "post-fx"]
# Nested inline table inside dotted section
[engine.renderer.pipeline.stage.config.viewport]
width = 1920
height = 1080
settings = { vsync = true, hdr = false, gamma = 2.2 }
# Hyphenated keys at multiple levels
[engine.audio-system.spatial-audio]
enabled = true
max-sources = 64
falloff-curve = "exponential"
rolloff-factor = 1.0e+0
# Map with pointer values using dotted headers
[game.entities.player]
health = 100
position.x = 0.0
position.y = -9.81e-1
position.z = 0.0
tags = ["controllable", "damageable"]
inventory = { slots = 20, weight_limit = 150.5 }
[game.entities.enemy-boss]
health = 5000
position.x = 100.0
position.y = 0.0
position.z = -50.0
tags = ["hostile", "boss", "damageable"]
ai = { aggression = 0.9, patrol_radius = 25 }
[game.entities."فارسی-test"]
health = 1
position.x = 1.0
position.y = 1.0
position.z = 1.0
tags = []
# Nested map of maps
[game.levels.level-01.zones.spawn-area]
bounds.min.x = -10
bounds.min.y = 0
bounds.min.z = -10
bounds.max.x = 10
bounds.max.y = 5
bounds.max.z = 10
enemy_count = 0
is_safe = true
[game.levels.level-01.zones.combat-zone]
bounds.min.x = 50
bounds.min.y = 0
bounds.min.z = 50
bounds.max.x = 150
bounds.max.y = 20
bounds.max.z = 150
enemy_count = 25
is_safe = false
# Array of tables with nested complexity
[[game.waves]]
id = 1
delay_ms = 0
spawns = [
{ entity = "enemy-grunt", count = 5, position = { x = 10.0, y = 0.0, z = 10.0 } },
{ entity = "enemy-scout", count = 3, position = { x = -10.0, y = 0.0, z = 10.0 } }
]
[[game.waves]]
id = 2
delay_ms = 30000
spawns = [
{ entity = "enemy-boss", count = 1, position = { x = 0.0, y = 0.0, z = 50.0 } }
]
# Deeply nested with mixed inline and standard tables
[physics.collision.layers.player-projectiles]
mask = 0b1010
priority = 10
callbacks.on_enter = "HandleProjectileHit"
callbacks.on_exit = "CleanupProjectile"
[physics.collision.layers.environment]
mask = 0b1111
priority = 1
callbacks.on_enter = "HandleCollision"
callbacks.on_exit = ""
# Scientific notation stress test
[constants]
planck = 6.62607015e-34
c = 2.998e+8
epsilon_0 = 8.854e-12
very_small = 1e-100
very_large = 1e+100
negative_exp = -5.5e-10
# Empty and edge cases mixed in
[edge.cases]
empty_string = ""
empty_array = []
empty_inline = {}
zero_int = 0
zero_float = 0.0
negative_int = -42
negative_float = -273.15
unicode_value = "日本語テスト 🎮 Ελληνικά"
hex_val = 0xDEAD
octal_val = 0o755
binary_val = 0b1010
`)
type Vec3 struct {
X float64 `toml:"x"`
Y float64 `toml:"y"`
Z float64 `toml:"z"`
}
type Bounds struct {
Min Vec3 `toml:"min"`
Max Vec3 `toml:"max"`
}
type ViewportSettings struct {
Vsync bool `toml:"vsync"`
HDR bool `toml:"hdr"`
Gamma float64 `toml:"gamma"`
}
type Viewport struct {
Width int `toml:"width"`
Height int `toml:"height"`
Settings ViewportSettings `toml:"settings"`
}
type StageConfig struct {
Name string `toml:"name"`
Priority int `toml:"priority"`
Enabled bool `toml:"enabled"`
ScaleFactor float64 `toml:"scale_factor"`
Tags []string `toml:"tags"`
Viewport Viewport `toml:"viewport"`
}
type Stage struct {
Config StageConfig `toml:"config"`
}
type Pipeline struct {
Stage Stage `toml:"stage"`
}
type Renderer struct {
Pipeline Pipeline `toml:"pipeline"`
}
type SpatialAudio struct {
Enabled bool `toml:"enabled"`
MaxSources int `toml:"max-sources"`
FalloffCurve string `toml:"falloff-curve"`
RolloffFactor float64 `toml:"rolloff-factor"`
}
type AudioSystem struct {
SpatialAudio SpatialAudio `toml:"spatial-audio"`
}
type Engine struct {
Renderer Renderer `toml:"renderer"`
AudioSystem AudioSystem `toml:"audio-system"`
}
type EntityConfig struct {
Health int `toml:"health"`
Position Vec3 `toml:"position"`
Tags []string `toml:"tags"`
Inventory map[string]any `toml:"inventory,omitempty"`
AI map[string]any `toml:"ai,omitempty"`
}
type Zone struct {
Bounds Bounds `toml:"bounds"`
EnemyCount int `toml:"enemy_count"`
IsSafe bool `toml:"is_safe"`
}
type Level struct {
Zones map[string]*Zone `toml:"zones"`
}
type SpawnPoint struct {
Entity string `toml:"entity"`
Count int `toml:"count"`
Position map[string]any `toml:"position"`
}
type Wave struct {
ID int `toml:"id"`
DelayMs int `toml:"delay_ms"`
Spawns []SpawnPoint `toml:"spawns"`
}
type Game struct {
Entities map[string]*EntityConfig `toml:"entities"`
Levels map[string]*Level `toml:"levels"`
Waves []*Wave `toml:"waves"`
}
type Callbacks struct {
OnEnter string `toml:"on_enter"`
OnExit string `toml:"on_exit"`
}
type CollisionLayer struct {
Mask int `toml:"mask"`
Priority int `toml:"priority"`
Callbacks Callbacks `toml:"callbacks"`
}
type Collision struct {
Layers map[string]*CollisionLayer `toml:"layers"`
}
type Physics struct {
Collision Collision `toml:"collision"`
}
type Constants struct {
Planck float64 `toml:"planck"`
C float64 `toml:"c"`
Epsilon0 float64 `toml:"epsilon_0"`
VerySmall float64 `toml:"very_small"`
VeryLarge float64 `toml:"very_large"`
NegativeExp float64 `toml:"negative_exp"`
}
type EdgeCases struct {
EmptyString string `toml:"empty_string"`
EmptyArray []any `toml:"empty_array"`
EmptyInline map[string]any `toml:"empty_inline"`
ZeroInt int `toml:"zero_int"`
ZeroFloat float64 `toml:"zero_float"`
NegativeInt int `toml:"negative_int"`
NegativeFloat float64 `toml:"negative_float"`
UnicodeValue string `toml:"unicode_value"`
HexVal int `toml:"hex_val"`
OctalVal int `toml:"octal_val"`
BinaryVal int `toml:"binary_val"`
}
type Edge struct {
Cases EdgeCases `toml:"cases"`
}
type Config struct {
Version string `toml:"version"`
Debug bool `toml:"debug"`
TickRate int `toml:"tick_rate"`
DeltaTime float64 `toml:"delta_time"`
Engine Engine `toml:"engine"`
Game Game `toml:"game"`
Physics Physics `toml:"physics"`
Constants Constants `toml:"constants"`
Edge Edge `toml:"edge"`
}
var cfg Config
if err := Unmarshal(input, &cfg); err != nil {
t.Fatalf("Unmarshal failed: %v", err)
}
// Root level
if cfg.Version != "2.0.0-beta" {
t.Errorf("Version = %q", cfg.Version)
}
if !cfg.Debug {
t.Error("Debug should be true")
}
if cfg.TickRate != 144 {
t.Errorf("TickRate = %d", cfg.TickRate)
}
// 5-level deep dotted header
sc := cfg.Engine.Renderer.Pipeline.Stage.Config
if sc.Name != "deferred" {
t.Errorf("Stage.Config.Name = %q", sc.Name)
}
if sc.ScaleFactor != 1.5e-2 {
t.Errorf("ScaleFactor = %e", sc.ScaleFactor)
}
if len(sc.Tags) != 3 || sc.Tags[1] != "shadows" {
t.Errorf("Stage tags = %v", sc.Tags)
}
if sc.Viewport.Width != 1920 {
t.Errorf("Viewport.Width = %d", sc.Viewport.Width)
}
if sc.Viewport.Settings.Gamma != 2.2 {
t.Errorf("Viewport.Settings.Gamma = %f", sc.Viewport.Settings.Gamma)
}
// Hyphenated keys
sa := cfg.Engine.AudioSystem.SpatialAudio
if sa.MaxSources != 64 {
t.Errorf("MaxSources = %d", sa.MaxSources)
}
if sa.FalloffCurve != "exponential" {
t.Errorf("FalloffCurve = %q", sa.FalloffCurve)
}
// Map pointer values with dotted keys inside
player := cfg.Game.Entities["player"]
if player == nil {
t.Fatal("player entity nil")
}
if player.Health != 100 {
t.Errorf("player.Health = %d", player.Health)
}
if player.Position.Y != -9.81e-1 {
t.Errorf("player.Positions.Y = %e", player.Position.Y)
}
if len(player.Tags) != 2 {
t.Errorf("player.Tags = %v", player.Tags)
}
boss := cfg.Game.Entities["enemy-boss"]
if boss == nil {
t.Fatal("enemy-boss entity nil")
}
if boss.Health != 5000 {
t.Errorf("boss.Health = %d", boss.Health)
}
// Unicode key (edge case)
unicode := cfg.Game.Entities["فارسی-test"]
if unicode == nil {
t.Fatal("unicode entity nil")
}
if unicode.Health != 1 {
t.Errorf("unicode.Health = %d", unicode.Health)
}
// Deeply nested map of maps
lvl := cfg.Game.Levels["level-01"]
if lvl == nil {
t.Fatal("level-01 nil")
}
spawn := lvl.Zones["spawn-area"]
if spawn == nil {
t.Fatal("spawn-area nil")
}
if spawn.Bounds.Min.X != -10 {
t.Errorf("spawn.Bounds.Min.X = %f", spawn.Bounds.Min.X)
}
if spawn.Bounds.Max.Y != 5 {
t.Errorf("spawn.Bounds.Max.Y = %f", spawn.Bounds.Max.Y)
}
if !spawn.IsSafe {
t.Error("spawn.IsSafe should be true")
}
combat := lvl.Zones["combat-zone"]
if combat == nil {
t.Fatal("combat-zone nil")
}
if combat.EnemyCount != 25 {
t.Errorf("combat.EnemyCount = %d", combat.EnemyCount)
}
// Array of tables with pointer slice
if len(cfg.Game.Waves) != 2 {
t.Fatalf("Waves count = %d", len(cfg.Game.Waves))
}
w1 := cfg.Game.Waves[0]
if w1.ID != 1 || w1.DelayMs != 0 {
t.Errorf("Wave[0] = %+v", w1)
}
if len(w1.Spawns) != 2 {
t.Errorf("Wave[0].Spawns count = %d", len(w1.Spawns))
}
if w1.Spawns[0].Entity != "enemy-grunt" || w1.Spawns[0].Count != 5 {
t.Errorf("Wave[0].Spawns[0] = %+v", w1.Spawns[0])
}
w2 := cfg.Game.Waves[1]
if w2.DelayMs != 30000 {
t.Errorf("Wave[1].DelayMs = %d", w2.DelayMs)
}
// Collision layers map
projLayer := cfg.Physics.Collision.Layers["player-projectiles"]
if projLayer == nil {
t.Fatal("player-projectiles layer nil")
}
if projLayer.Mask != 0b1010 {
t.Errorf("projLayer.Mask = %d", projLayer.Mask)
}
if projLayer.Callbacks.OnEnter != "HandleProjectileHit" {
t.Errorf("projLayer.Callbacks.OnEnter = %q", projLayer.Callbacks.OnEnter)
}
// Scientific notation
if cfg.Constants.Planck != 6.62607015e-34 {
t.Errorf("Planck = %e", cfg.Constants.Planck)
}
if cfg.Constants.C != 2.998e+8 {
t.Errorf("C = %e", cfg.Constants.C)
}
if cfg.Constants.VerySmall != 1e-100 {
t.Errorf("VerySmall = %e", cfg.Constants.VerySmall)
}
if cfg.Constants.NegativeExp != -5.5e-10 {
t.Errorf("NegativeExp = %e", cfg.Constants.NegativeExp)
}
// Edge cases
if cfg.Edge.Cases.EmptyString != "" {
t.Errorf("EmptyString = %q", cfg.Edge.Cases.EmptyString)
}
if len(cfg.Edge.Cases.EmptyArray) != 0 {
t.Errorf("EmptyArray = %v", cfg.Edge.Cases.EmptyArray)
}
if len(cfg.Edge.Cases.EmptyInline) != 0 {
t.Errorf("EmptyInline = %v", cfg.Edge.Cases.EmptyInline)
}
if cfg.Edge.Cases.NegativeInt != -42 {
t.Errorf("NegativeInt = %d", cfg.Edge.Cases.NegativeInt)
}
if cfg.Edge.Cases.NegativeFloat != -273.15 {
t.Errorf("NegativeFloat = %f", cfg.Edge.Cases.NegativeFloat)
}
if cfg.Edge.Cases.UnicodeValue != "日本語テスト 🎮 Ελληνικά" {
t.Errorf("UnicodeValue = %q", cfg.Edge.Cases.UnicodeValue)
}
if cfg.Edge.Cases.HexVal != 0xDEAD {
t.Errorf("HexVal = %d, want %d", cfg.Edge.Cases.HexVal, 0xDEAD)
}
if cfg.Edge.Cases.OctalVal != 0o755 {
t.Errorf("OctalVal = %d, want %d", cfg.Edge.Cases.OctalVal, 0o755)
}
if cfg.Edge.Cases.BinaryVal != 0b1010 {
t.Errorf("BinaryVal = %d, want %d", cfg.Edge.Cases.BinaryVal, 0b1010)
}
}