package xmlparser import ( "bytes" "fmt" "strings" "sync" "testing" ) // Benchmark data of different sizes var ( smallXML = ` Small Test Small value ` mediumXML = ` Medium Test Document This is a medium-sized XML document for benchmarking purposes
Medium XML document with nested elements benchmark medium xml benchmark test
2025-01-09 Test Suite 1.0
` // Generate large XML dynamically in benchmarks to avoid bloating the source largeXMLTemplate = ` Large Test Document This is a large XML document for benchmarking performance %s ` ) // generateLargeXML creates a large XML document for benchmarking func generateLargeXML() string { var buffer strings.Builder // Generate repetitive data to create a large document for i := 0; i < 1000; i++ { buffer.WriteString(fmt.Sprintf(` Item %d Title This is item number %d with detailed description for benchmarking purposes. It contains enough text to make the document significantly larger. category-%d tag1 tag2 tag3 `, i, i, i, i%10)) } return fmt.Sprintf(largeXMLTemplate, buffer.String()) } // BenchmarkParseString benchmarks parsing from string func BenchmarkParseString(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(smallXML) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkParseBytes benchmarks parsing from byte slice func BenchmarkParseBytes(b *testing.B) { parser := NewParser[MockParsable](nil) xmlData := []byte(smallXML) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseBytes(xmlData) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkParse benchmarks parsing from reader func BenchmarkParse(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() for i := 0; i < b.N; i++ { reader := strings.NewReader(smallXML) _, err := parser.Parse(reader) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkParseSmallXML benchmarks parsing small XML documents func BenchmarkParseSmallXML(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(smallXML) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkParseMediumXML benchmarks parsing medium XML documents func BenchmarkParseMediumXML(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(mediumXML) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkParseLargeXML benchmarks parsing large XML documents func BenchmarkParseLargeXML(b *testing.B) { parser := NewParser[MockParsable](nil) largeXML := generateLargeXML() b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(largeXML) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkParseWithResult benchmarks parsing with detailed results func BenchmarkParseWithResult(b *testing.B) { parser := NewParser[MockParsable](nil) genericParser := parser.(*GenericParser[MockParsable]) b.ResetTimer() for i := 0; i < b.N; i++ { reader := strings.NewReader(mediumXML) _, err := genericParser.ParseWithResult(reader) if err != nil { b.Errorf("Parse error: %v", err) } } } // BenchmarkValidation benchmarks XML validation func BenchmarkValidation(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() for i := 0; i < b.N; i++ { reader := strings.NewReader(mediumXML) err := parser.Validate(reader) if err != nil { b.Errorf("Validation error: %v", err) } } } // BenchmarkConcurrentParsing benchmarks concurrent parsing performance func BenchmarkConcurrentParsing(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { _, err := parser.ParseString(smallXML) if err != nil { b.Errorf("Parse error: %v", err) } } }) } // BenchmarkConcurrentParsingWithWaitGroup benchmarks coordinated concurrent parsing func BenchmarkConcurrentParsingWithWaitGroup(b *testing.B) { parser := NewParser[MockParsable](nil) b.ResetTimer() for i := 0; i < b.N; i++ { var wg sync.WaitGroup numGoroutines := 4 for j := 0; j < numGoroutines; j++ { wg.Add(1) go func() { defer wg.Done() _, err := parser.ParseString(smallXML) if err != nil { b.Errorf("Parse error: %v", err) } }() } wg.Wait() } } // BenchmarkMemoryAllocation benchmarks memory allocation patterns func BenchmarkMemoryAllocation(b *testing.B) { parser := NewParser[MockParsable](nil) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { result, err := parser.ParseString(mediumXML) if err != nil { b.Errorf("Parse error: %v", err) } // Use result to prevent optimization _ = result.GetID() } } // BenchmarkMemoryAllocationsLarge benchmarks memory allocations for large documents func BenchmarkMemoryAllocationsLarge(b *testing.B) { parser := NewParser[MockParsable](nil) largeXML := generateLargeXML() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { result, err := parser.ParseString(largeXML) if err != nil { b.Errorf("Parse error: %v", err) } // Use result to prevent optimization _ = result.GetID() } } // BenchmarkSanitizeXMLString benchmarks XML string sanitization func BenchmarkSanitizeXMLString(b *testing.B) { input := "Test string with\x00invalid\x01characters\x02and normal text" b.ResetTimer() for i := 0; i < b.N; i++ { result := SanitizeXMLString(input) // Use result to prevent optimization _ = len(result) } } // BenchmarkValidateXMLName benchmarks XML name validation func BenchmarkValidateXMLName(b *testing.B) { testNames := []string{ "ValidElement", "invalid-element-123", "_privateElement", "123Invalid", "element-with-hyphen", } b.ResetTimer() for i := 0; i < b.N; i++ { name := testNames[i%len(testNames)] result := ValidateXMLName(name) // Use result to prevent optimization _ = result } } // BenchmarkIsValidNamespace benchmarks namespace validation func BenchmarkIsValidNamespace(b *testing.B) { testNamespaces := []string{ "http://example.com", "https://ted.europa.eu/schema", "urn:oasis:names:specification:ubl", "invalid-namespace", "", } b.ResetTimer() for i := 0; i < b.N; i++ { namespace := testNamespaces[i%len(testNamespaces)] result := IsValidNamespace(namespace) // Use result to prevent optimization _ = result } } // BenchmarkNormalizeWhitespace benchmarks whitespace normalization func BenchmarkNormalizeWhitespace(b *testing.B) { input := " Multiple \t\n whitespace\r\n characters " b.ResetTimer() for i := 0; i < b.N; i++ { result := NormalizeWhitespace(input) // Use result to prevent optimization _ = len(result) } } // BenchmarkTruncateString benchmarks string truncation func BenchmarkTruncateString(b *testing.B) { input := "This is a very long string that needs to be truncated for display purposes" maxLength := 50 b.ResetTimer() for i := 0; i < b.N; i++ { result := TruncateString(input, maxLength) // Use result to prevent optimization _ = len(result) } } // BenchmarkParserCreation benchmarks parser creation func BenchmarkParserCreation(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { parser := NewParser[MockParsable](nil) // Use parser to prevent optimization _ = parser } } // BenchmarkParserCreationWithOptions benchmarks parser creation with custom options func BenchmarkParserCreationWithOptions(b *testing.B) { options := &ParserOptions{ StrictValidation: false, AllowEmptyElements: true, PreserveWhitespace: false, IgnoreUnknownElements: true, MaxDepth: 100, MaxSize: 10 * 1024 * 1024, } b.ResetTimer() for i := 0; i < b.N; i++ { parser := NewParser[MockParsable](options) // Use parser to prevent optimization _ = parser } } // BenchmarkValidationError benchmarks validation error creation func BenchmarkValidationError(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { err := ValidationError{ Field: "testField", Message: "test error message", Value: "test value", } // Use error to prevent optimization _ = err.Error() } } // BenchmarkValidationErrors benchmarks multiple validation errors func BenchmarkValidationErrors(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { var errors ValidationErrors errors.Add("field1", "error1", "value1") errors.Add("field2", "error2", "value2") errors.Add("field3", "error3", "value3") // Use errors to prevent optimization _ = errors.Error() } } // BenchmarkMockValidation benchmarks mock object validation func BenchmarkMockValidation(b *testing.B) { mock := MockParsable{ ID: "test-123", Name: "Test Mock", Value: "test value", Valid: true, } b.ResetTimer() for i := 0; i < b.N; i++ { err := mock.Validate() // Use error to prevent optimization _ = err } } // BenchmarkDifferentXMLSizes compares performance across different document sizes func BenchmarkDifferentXMLSizes(b *testing.B) { parser := NewParser[MockParsable](nil) testCases := []struct { name string xml string }{ {"Small", smallXML}, {"Medium", mediumXML}, } // Add large XML dynamically to avoid memory overhead during compilation largeXML := generateLargeXML() testCases = append(testCases, struct { name string xml string }{"Large", largeXML}) for _, tc := range testCases { b.Run(tc.name, func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(tc.xml) if err != nil { b.Errorf("Parse error: %v", err) } } }) } } // BenchmarkParsingMethods compares different parsing methods func BenchmarkParsingMethods(b *testing.B) { parser := NewParser[MockParsable](nil) xmlData := []byte(mediumXML) b.Run("ParseString", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(mediumXML) if err != nil { b.Errorf("Parse error: %v", err) } } }) b.Run("ParseBytes", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseBytes(xmlData) if err != nil { b.Errorf("Parse error: %v", err) } } }) b.Run("Parse", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { reader := bytes.NewReader(xmlData) _, err := parser.Parse(reader) if err != nil { b.Errorf("Parse error: %v", err) } } }) } // BenchmarkUtilityFunctions benchmarks all utility functions together func BenchmarkUtilityFunctions(b *testing.B) { testData := struct { dirtyString string xmlName string namespace string whitespace string longString string }{ dirtyString: "Dirty\x00string\x01with\x02control\x03characters", xmlName: "ValidElementName", namespace: "http://example.com/namespace", whitespace: " Multiple \t\n spaces ", longString: "This is a very long string that needs truncation for testing purposes", } b.ResetTimer() for i := 0; i < b.N; i++ { // Run all utility functions _ = SanitizeXMLString(testData.dirtyString) _ = ValidateXMLName(testData.xmlName) _ = IsValidNamespace(testData.namespace) _ = NormalizeWhitespace(testData.whitespace) _ = TruncateString(testData.longString, 50) } } // BenchmarkParserOptionsEffects benchmarks parsing with different options func BenchmarkParserOptionsEffects(b *testing.B) { testCases := []struct { name string options *ParserOptions }{ { name: "Default", options: nil, }, { name: "StrictDisabled", options: &ParserOptions{ StrictValidation: false, }, }, { name: "PreserveWhitespace", options: &ParserOptions{ PreserveWhitespace: true, }, }, { name: "IgnoreUnknown", options: &ParserOptions{ IgnoreUnknownElements: true, }, }, } for _, tc := range testCases { b.Run(tc.name, func(b *testing.B) { parser := NewParser[MockParsable](tc.options) b.ResetTimer() for i := 0; i < b.N; i++ { _, err := parser.ParseString(mediumXML) if err != nil { b.Errorf("Parse error: %v", err) } } }) } } // BenchmarkErrorHandling benchmarks error creation and handling func BenchmarkErrorHandling(b *testing.B) { b.Run("ValidationError", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { err := ValidationError{ Field: "testField", Message: "test message", Value: "test value", } _ = err.Error() } }) b.Run("XMLParseError", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { err := XMLParseError{ Line: 10, Column: 5, Element: "TestElement", Message: "test parse error", } _ = err.Error() } }) b.Run("ValidationErrors", func(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { var errors ValidationErrors errors.Add("field1", "message1", "value1") errors.Add("field2", "message2", "value2") _ = errors.Error() } }) } // BenchmarkMemoryEfficiency measures memory efficiency across operations func BenchmarkMemoryEfficiency(b *testing.B) { parser := NewParser[MockParsable](nil) b.Run("SmallDocuments", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { result, err := parser.ParseString(smallXML) if err != nil { b.Errorf("Parse error: %v", err) } // Ensure result is used _ = result.GetID() } }) b.Run("MediumDocuments", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { result, err := parser.ParseString(mediumXML) if err != nil { b.Errorf("Parse error: %v", err) } // Ensure result is used _ = result.GetID() } }) }