Skip to main content

Test Overview

FG Character Extractor uses a golden file testing pattern to validate XML extraction. This approach compares generated output against pre-validated expected files to ensure consistent, correct extraction.
Tests are located in main_test.go with test data in the tests/ directory.

Running Tests

Run the test suite using either Task or Go directly:
task test
The -v flag provides verbose output showing each test step. Remove it for quieter output.

Test Structure

The project includes one comprehensive integration test that validates the entire extraction pipeline.

Test Files

tests/
├── db.xml                                 # Input: Sample FG campaign database
└── expected_character_id-00001_4.xml      # Expected output: Golden file

Test Flow

The TestRun function in main_test.go:10-43 follows this pattern:
1

Setup and Cleanup

defer os.Remove("character_id-00001_4.xml")
Ensures generated files are cleaned up after the test, even if it fails.
2

Execute Extraction

if err := run("tests/db.xml"); err != nil {
    t.Fatalf("run() failed: %v", err)
}
Runs the extraction against the test database file.
3

Verify File Creation

if _, err := os.Stat(expectedOutput); os.IsNotExist(err) {
    t.Fatalf("Expected output file %s was not created", expectedOutput)
}
Confirms the output file was generated.
4

Compare with Golden File

if !bytes.Equal(expectedContent, generatedContent) {
    t.Errorf("Generated content does not match golden file")
}
Performs byte-for-byte comparison with the expected output.

Golden File Pattern

The golden file approach provides several benefits:

Comprehensive Validation

Validates the entire output structure, not just specific fields

Regression Detection

Catches unintended changes to output format

Easy Updates

When output format changes intentionally, update the golden file

Visual Diffs

Failed tests can be inspected with standard diff tools

How Golden Files Work

  1. Initial Creation: Generate expected output manually, verify it’s correct, save as expected_*.xml
  2. Test Execution: Run extraction and compare output to golden file
  3. Validation: Byte-for-byte comparison ensures exact match
  4. Updates: When output format changes, regenerate and review golden files

Adding New Tests

To add a new test case:
1

Create test input

Add a new Fantasy Grounds database XML file to tests/:
tests/new_scenario.xml
2

Generate expected output

Run the extraction manually to generate the output:
go run main.go tests/new_scenario.xml
3

Verify and save golden file

Review the generated character_*.xml file carefully. If correct, move it to the tests directory:
mv character_id-00002_5.xml tests/expected_character_id-00002_5.xml
4

Write the test function

Add a new test function to main_test.go:
func TestRunMultipleCharacters(t *testing.T) {
    expectedOutput := "character_id-00002_5.xml"
    defer os.Remove(expectedOutput)
    
    if err := run("tests/new_scenario.xml"); err != nil {
        t.Fatalf("run() failed: %v", err)
    }
    
    goldenFile := filepath.Join("tests", "expected_"+expectedOutput)
    expectedContent, err := os.ReadFile(goldenFile)
    if err != nil {
        t.Fatalf("Failed to read golden file: %v", err)
    }
    
    generatedContent, err := os.ReadFile(expectedOutput)
    if err != nil {
        t.Fatalf("Failed to read generated file: %v", err)
    }
    
    if !bytes.Equal(expectedContent, generatedContent) {
        t.Errorf("Content mismatch for %s", expectedOutput)
    }
}
5

Run and validate

task test

Debugging Test Failures

When tests fail, follow these debugging steps:
Use a diff tool to see what changed:
diff character_id-00001_4.xml tests/expected_character_id-00001_4.xml
Or use a visual diff tool:
code --diff character_id-00001_4.xml tests/expected_character_id-00001_4.xml
Compare file sizes to identify major discrepancies:
ls -lh character_id-00001_4.xml tests/expected_character_id-00001_4.xml
Validate that both files are well-formed XML:
xmllint --format character_id-00001_4.xml > generated_formatted.xml
xmllint --format tests/expected_character_id-00001_4.xml > expected_formatted.xml
diff generated_formatted.xml expected_formatted.xml
If the test previously passed, check what changed:
git diff main.go
git log --oneline -10
If the output change is intentional and correct:
# Verify the new output is correct
cat character_id-00001_4.xml

# Update the golden file
cp character_id-00001_4.xml tests/expected_character_id-00001_4.xml

# Run tests again
task test

Test Data Structure

The test database (tests/db.xml) contains:
  • Fantasy Grounds campaign data with character sheets
  • Sample character with ID id-00001 at level 4
  • Ability scores and bonuses for skill calculation testing
  • Skill list data to validate proficiency bonus calculations
  • Tags to filter (<public>, <holder*>) to test exclusion logic
The expected output (tests/expected_character_id-00001_4.xml) validates:
  • Correct XML structure with proper root element
  • Character data extraction and filtering
  • Skill total calculation (ability bonus + proficiency bonus)
  • Special character escaping (&, <, >)
  • Proper XML formatting and indentation

Running Tests in CI/CD

For continuous integration, run tests with additional flags:
go test -v -race -coverprofile=coverage.out ./...
The -race flag detects race conditions, and -coverprofile generates coverage data.

Test Coverage

Check test coverage:
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
This opens an HTML report showing which lines are covered by tests.

Next Steps

Building

Learn about build commands and cross-platform compilation

Contributing

Review contribution guidelines and workflow

Build docs developers (and LLMs) love