Files
golib/archive/TESTING.md
nabbar 344498a7d8 Improvements, test & documentatons (2025-11 #3)
[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
2025-11-22 18:04:16 +01:00

14 KiB

Testing Guide

License: MIT Go Version Tests Coverage

Comprehensive testing documentation for the archive package, covering test execution, race detection, and quality assurance.


Table of Contents


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/AfterEach for setup/cleanup
  • Avoid global mutable state
  • Create test data on-demand
  • Don't rely on test execution order

Test Data

  • Use loremIpsum constant for text data (302KB)
  • Generate archives in BeforeSuite or 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 GinkgoWriter for 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 -race during 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_test convention 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