[root] - UPDATE documentation: enhanced README and TESTING guidelines - UPDATE security md file: fix minimal go version needed - ADD script: add coverage_report.sh script (see TESTING for info) [ioutils/aggregator] - ADD package: add new package to simplify aggregation of multiple write to a unique writer function - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [router] - UPDATE documentation [semaphore] - FIX bug if given context is nil or have error trigger [shell] - UPDATE package & sub-package: fix bugs and optimize code - ADD sub-package tty: allow to backup and restore tty setting - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [socket] - UPDATE package & sub-package: rename function Handler to HandlerFunc - UPDATE package & sub-package: add new interface Handler to expose a socket compatible handler function [Other] - UPDATE go.mod: bump dependencies
14 KiB
Testing Guide
Comprehensive testing documentation for the archive package, covering test execution, race detection, and quality assurance.
Table of Contents
- Overview
- Quick Start
- Test Framework
- Running Tests
- Test Coverage
- Thread Safety
- Writing Tests
- Best Practices
- Troubleshooting
- CI Integration
Overview
The archive package uses Ginkgo v2 (BDD testing framework) and Gomega (matcher library) for comprehensive testing with expressive assertions.
Test Suite
- Total Specs: 112
- Coverage: ≥80%
- Race Detection: ✅ Zero data races
- Execution Time: ~6s (without race), ~13s (with race)
Coverage Areas
- Archive operations (TAR, ZIP)
- Compression algorithms (GZIP, BZIP2, LZ4, XZ)
- Helper pipelines with thread safety
- Auto-detection and extraction
- Error handling and edge cases
Quick Start
# Install Ginkgo CLI (optional)
go install github.com/onsi/ginkgo/v2/ginkgo@latest
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Run with race detection (recommended)
CGO_ENABLED=1 go test -race ./...
# Using Ginkgo CLI
ginkgo -cover -race
Test Framework
Ginkgo v2 - BDD testing framework (docs)
- Hierarchical test organization (
Describe,Context,It) - Setup/teardown hooks (
BeforeEach,AfterEach,BeforeSuite,AfterSuite) - Parallel execution support
- Rich CLI with filtering
Gomega - Matcher library (docs)
- Readable assertion syntax
- Extensive built-in matchers
- Detailed failure messages
Running Tests
Basic Commands
# Standard test run
go test ./...
# Verbose output
go test -v ./...
# With coverage
go test -cover ./...
# Generate HTML coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
Ginkgo CLI Options
# Run all tests
ginkgo
# Specific test file
ginkgo --focus-file=interface_test.go
# Pattern matching
ginkgo --focus="compression"
# Parallel execution
ginkgo -p
# JUnit report
ginkgo --junit-report=results.xml
Race Detection
Critical for concurrent operations testing
# Enable race detector (requires CGO)
CGO_ENABLED=1 go test -race ./...
# With Ginkgo
CGO_ENABLED=1 ginkgo -race
Validates:
- Atomic operations (
atomic.Bool) - Mutex protection (
sync.Mutex) - Goroutine synchronization (
sync.WaitGroup) - Buffer thread safety
Expected Output:
# ✅ Success
ok github.com/nabbar/golib/archive 12.859s
# ❌ Race detected
WARNING: DATA RACE
Read at 0x... by goroutine ...
Status: Zero data races detected
Performance & Profiling
# Benchmarks
go test -bench=. -benchmem ./...
# Memory profiling
go test -memprofile=mem.out ./...
go tool pprof mem.out
# CPU profiling
go test -cpuprofile=cpu.out ./...
go tool pprof cpu.out
Performance Expectations
| Test Type | Duration | Notes |
|---|---|---|
| Full Suite | ~6s | Without race |
With -race |
~13s | 2x slower (normal) |
| Individual Spec | <100ms | Most tests |
| Compression | 200-500ms | Algorithm-dependent |
Test Coverage
Target: ≥80% statement coverage
Coverage By Category
| Category | Files | Description |
|---|---|---|
| Compression | compression_algorithms_test.go, archive_{gzip,bzip,lz4,xz}_test.go |
All algorithms, header detection, properties |
| Archives | archive_tar_test.go, archive_zip_test.go |
Creation, extraction, listing, walk |
| Helper | helper_compress_test.go, helper_advanced_test.go |
Pipelines, streaming, edge cases |
| Extraction | extract_test.go |
Auto-detection, path security |
| Interface | interface_test.go |
Parse/Detect wrappers |
| Errors | error_handling_test.go |
Invalid inputs, corruption |
| Constants | archive_const_test.go |
Algorithm enums, marshaling |
View Coverage
# Generate coverage report
go test -coverprofile=coverage.out ./...
# View in terminal
go tool cover -func=coverage.out
# Generate HTML report
go tool cover -html=coverage.out -o coverage.html
Test Structure
Tests follow Ginkgo's hierarchical BDD structure:
Describe("archive/component", func() {
BeforeSuite(func() {
// Global setup
})
AfterSuite(func() {
// Global cleanup
})
Context("Feature or scenario", func() {
BeforeEach(func() {
// Per-test setup
})
AfterEach(func() {
// Per-test cleanup
})
It("should do something specific", func() {
// Test implementation
Expect(result).To(Equal(expected))
})
})
})
Thread Safety
Thread safety is critical for the helper subpackage's concurrent operations.
Concurrency Primitives
// Atomic state flags
atomic.Bool
// Buffer protection
sync.Mutex
// Goroutine lifecycle
sync.WaitGroup
Verified Components
| Component | Mechanism | Status |
|---|---|---|
helper.deCompressWriter |
atomic.Bool + sync.WaitGroup |
✅ Race-free |
helper.bufNoEOF |
sync.Mutex + atomic.Bool |
✅ Race-free |
| Compression Pipelines | Independent goroutines | ✅ Parallel-safe |
Testing Commands
# Full suite with race detection
CGO_ENABLED=1 go test -race -v ./...
# Focus on concurrent operations
CGO_ENABLED=1 go test -race -v -run "Helper" ./...
# Stress test
for i in {1..10}; do CGO_ENABLED=1 go test -race ./... || break; done
Result: Zero data races across all test runs
Test File Organization
| File | Purpose | Specs |
|---|---|---|
archive_suite_test.go |
Suite initialization | 1 |
archive_const_test.go |
Algorithm constants | 8 |
archive_tar_test.go |
TAR operations | 15 |
archive_zip_test.go |
ZIP operations | 15 |
archive_{gzip,bzip,lz4,xz}_test.go |
Algorithm-specific | 6 each |
archive_tgz_test.go |
TAR.GZ combined | 6 |
compression_algorithms_test.go |
Compression tests | 15 |
helper_compress_test.go |
Helper pipelines | 10 |
helper_advanced_test.go |
Advanced helper | 8 |
interface_test.go |
Interface wrappers | 6 |
extract_test.go |
Extraction | 7 |
error_handling_test.go |
Error cases | 12 |
lorem_ipsum_test.go |
Test data | 0 |
Writing Tests
Guidelines
1. Use Descriptive Names
It("should compress and decompress data without loss", func() {
// Test implementation
})
2. Follow AAA Pattern (Arrange, Act, Assert)
It("should detect gzip compression", func() {
// Arrange
var buf bytes.Buffer
writer, _ := arccmp.Gzip.Writer(&buf)
writer.Write([]byte("test"))
writer.Close()
// Act
alg, reader, err := libarc.DetectCompression(&buf)
// Assert
Expect(err).ToNot(HaveOccurred())
Expect(alg).To(Equal(arccmp.Gzip))
Expect(reader).ToNot(BeNil())
})
3. Use Appropriate Matchers
Expect(value).To(Equal(expected))
Expect(err).ToNot(HaveOccurred())
Expect(list).To(ContainElement(item))
Expect(number).To(BeNumerically(">", 0))
4. Always Cleanup Resources
defer reader.Close()
defer os.Remove(tempFile)
5. Test Edge Cases - Empty input, nil values, large data, etc.
6. Avoid External Dependencies - No remote resources or external services
Test Template
var _ = Describe("archive/new_feature", func() {
Context("When using new feature", func() {
var (
testData []byte
tempFile string
)
BeforeEach(func() {
testData = []byte("test data")
})
AfterEach(func() {
if tempFile != "" {
os.Remove(tempFile)
}
})
It("should perform expected behavior", func() {
// Arrange
input := prepareInput(testData)
// Act
result, err := newFeature(input)
// Assert
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(expectedResult))
})
It("should handle error case", func() {
_, err := newFeature(invalidInput)
Expect(err).To(HaveOccurred())
})
})
})
Best Practices
Test Independence
- ✅ Each test should be independent
- ✅ Use
BeforeEach/AfterEachfor setup/cleanup - ✅ Avoid global mutable state
- ✅ Create test data on-demand
- ❌ Don't rely on test execution order
Test Data
- Use
loremIpsumconstant for text data (302KB) - Generate archives in
BeforeSuiteor on-demand - Clean up in
AfterSuite - Use
filepath.Join()for cross-platform paths - Use
os.MkdirTemp()for isolation
Assertions
// ✅ Good
Expect(err).ToNot(HaveOccurred())
Expect(value).To(Equal(expected))
// ❌ Avoid
Expect(value == expected).To(BeTrue())
- Use specific matchers for better error messages
- One behavior per test
- Use
GinkgoWriterfor debug output
Concurrency Testing
It("should handle concurrent operations", func() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// Independent operation
}(i)
}
wg.Wait()
})
- Always run with
-raceduring development - Test concurrent operations explicitly
- Verify cleanup with
sync.WaitGroup - Use atomic operations and mutexes
Performance
- Keep tests fast (small data)
- Use parallel execution (
ginkgo -p) - Target: <6s full suite, <100ms per spec
Error Handling
// ✅ Good
It("should handle errors", func() {
result, err := operation()
Expect(err).ToNot(HaveOccurred())
defer result.Close()
})
// ❌ Bad
It("should do something", func() {
result, _ := operation() // Don't ignore errors!
})
Troubleshooting
Leftover Test Files
# Clean manually if needed
rm -f lorem_ipsum*.{txt,tar,zip,gz,bz2,lz4,xz}
rm -rf extract_all_dir
Stale Coverage
go clean -testcache
go test -coverprofile=coverage.out ./...
Parallel Test Failures
- Check for shared resources or global state
- Use synchronization or make tests independent
Import Cycles
- Use
package archive_testconvention to avoid cycles
Race Conditions
# Debug races
CGO_ENABLED=1 go test -race -v ./... 2>&1 | tee race-log.txt
grep -A 20 "WARNING: DATA RACE" race-log.txt
Check for:
- Unprotected shared variable access
- Missing mutex locks
- Unsynchronized goroutines
Example fix:
// ❌ Bad: Direct access
if o.b.Len() < 1 { // Race condition
// ✅ Good: Protected access
if o.Len() < 1 { // Len() uses mutex
CGO Not Available
# Install build tools
# Ubuntu/Debian: sudo apt-get install build-essential
# macOS: brew install gcc
export CGO_ENABLED=1
go test -race ./...
Test Timeouts
# Identify hanging tests
ginkgo --timeout=10s
Check for:
- Goroutine leaks (missing
wg.Done()) - Unclosed resources
- Mutex deadlocks
Debugging
# Single test
ginkgo --focus="should compress and decompress"
# Specific file
ginkgo --focus-file=extract_test.go
# Verbose output
ginkgo -v --trace
Use GinkgoWriter for debug output:
fmt.Fprintf(GinkgoWriter, "Debug: value = %v\n", value)
CI Integration
GitHub Actions Example
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run tests
run: go test -v ./...
- name: Race detection
run: CGO_ENABLED=1 go test -race ./...
- name: Coverage
run: go test -coverprofile=coverage.out ./...
Pre-commit Hook
#!/bin/bash
CGO_ENABLED=1 go test -race ./... || exit 1
go test -cover ./... | grep -E "coverage:" || exit 1
Quality Checklist
Before merging code:
- All tests pass:
go test ./... - Race detection clean:
CGO_ENABLED=1 go test -race ./... - Coverage maintained: ≥80%
- New features have tests
- Error cases tested
- Thread safety validated
- Test duration reasonable (<10s)
Resources
Testing Frameworks
Concurrency
Performance
AI Transparency Notice
In accordance with Article 50.4 of the EU AI Act, AI assistance has been used for testing, documentation, and bug fixing under human supervision.
Version: Go 1.18+ on Linux, macOS, Windows
Maintained By: Archive Package Contributors