Skip to content

Commit 7295c5c

Browse files
erraggyclaude
andauthored
feat(differ): add comprehensive benchmark suite for differ package (#23)
* feat(differ): add comprehensive benchmark suite for differ package Add benchmark tests for differ package with 10 benchmarks covering all major operations and performance patterns. Update documentation to reflect new benchmarks and performance metrics. Changes: - Add differ/differ_bench_test.go with 10 comprehensive benchmarks - Convenience functions (Diff, DiffParsed) - Struct-based API (Differ.Diff, Differ.DiffParsed) - Different modes (Simple, Breaking) - Configuration options (WithInfo, WithoutInfo) - Edge cases (IdenticalSpecs) - Parse-once pattern efficiency - Update benchmarks.md with differ performance metrics - Add Differ Performance section with detailed results - Update benchmark count from 60+ to 70+ - Update version from v1.7.0 to v1.9.1 in overview - Add differ to benchmark suite listing - Add differ to running benchmarks examples - Update best practices to include differ - Update README.md with differ benchmarks - Update benchmark count from 60+ to 70+ - Update version from v1.7.0 to v1.9.1 - Add 58x faster DiffParsed performance metric - Add Diff operation to performance table - Update Makefile with differ support - Add bench-differ target for running differ benchmarks - Add ./differ to all benchmark targets (bench, bench-save, bench-baseline, bench-cpu, bench-mem, bench-profile) - Add bench-differ to .PHONY targets Performance Results: - DiffParsed is 58x faster than Diff (7.8μs vs 457μs) - Identical specs comparison is very fast (2.8μs) - fast path working - Breaking mode vs Simple mode has negligible difference (~0.15μs) This update completes the benchmark coverage for all five core packages (parser, validator, converter, joiner, differ). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(differ): update benchmarks to use Go 1.24+ for b.Loop() pattern Address claude-review feedback by updating all differ benchmarks to use the modern Go 1.24+ for b.Loop() pattern for consistency with other packages. Also add comprehensive benchmark testing guidance to CLAUDE.md. Changes: - Update all 10 benchmarks to use for b.Loop() instead of the old for i := 0; i < b.N; i++ pattern - Remove redundant b.ReportAllocs() calls (handled by b.Loop()) - Remove unnecessary b.ResetTimer() calls for trivial setup - Add package-level comment about b.Fatalf usage pattern - Add new "Benchmark Test Requirements" section to CLAUDE.md - Document the for b.Loop() pattern as the required standard - Provide correct and incorrect examples - Explain why consistency matters - Add guidance on when to include setup outside the loop This ensures consistency across all five benchmark files: - parser/parser_bench_test.go: Uses for b.Loop() ✅ - validator/validator_bench_test.go: Uses for b.Loop() ✅ - converter/converter_bench_test.go: Uses for b.Loop() ✅ - joiner/joiner_bench_test.go: Uses for b.Loop() ✅ - differ/differ_bench_test.go: Now uses for b.Loop() ✅ Benchmark results remain consistent with previous run, confirming the pattern change has no negative impact on performance measurement. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ea1f590 commit 7295c5c

6 files changed

Lines changed: 388 additions & 12 deletions

File tree

CLAUDE.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,74 @@ func TestValidatorValidate(t *testing.T) { ... }
468468
func TestJoinerJoin(t *testing.T) { ... }
469469
func TestConverterConvert(t *testing.T) { ... }
470470
```
471+
472+
### Benchmark Test Requirements
473+
474+
**CRITICAL: Use the Go 1.24+ `for b.Loop()` pattern for all benchmarks.**
475+
476+
When writing benchmark tests, you MUST follow these requirements:
477+
478+
1. **Use `for b.Loop()` Pattern** (Go 1.24+)
479+
- **ALWAYS** use `for b.Loop()` instead of `for i := 0; i < b.N; i++`
480+
- **DO NOT** call `b.ReportAllocs()` manually (handled automatically by `b.Loop()`)
481+
- This is the modern, consistent pattern used across all packages
482+
483+
**Correct Pattern**:
484+
```go
485+
func BenchmarkDiffParsed(b *testing.B) {
486+
// Setup (parsing, creating instances, etc.)
487+
source, _ := parser.Parse("file.yaml", false, true)
488+
target, _ := parser.Parse("file2.yaml", false, true)
489+
490+
for b.Loop() { // ✅ Correct - modern Go 1.24+ pattern
491+
_, err := DiffParsed(*source, *target)
492+
if err != nil {
493+
b.Fatal(err)
494+
}
495+
}
496+
}
497+
```
498+
499+
**Incorrect Pattern**:
500+
```go
501+
func BenchmarkDiffParsed(b *testing.B) {
502+
source, _ := parser.Parse("file.yaml", false, true)
503+
target, _ := parser.Parse("file2.yaml", false, true)
504+
505+
b.ReportAllocs() // ❌ Wrong - redundant with b.Loop()
506+
for i := 0; i < b.N; i++ { // ❌ Wrong - old pattern
507+
_, err := DiffParsed(*source, *target)
508+
if err != nil {
509+
b.Fatal(err)
510+
}
511+
}
512+
}
513+
```
514+
515+
2. **No Manual Timer Resets**
516+
- **DO NOT** call `b.ResetTimer()` for trivial setup (e.g., creating a Differ instance)
517+
- `b.Loop()` handles timing automatically
518+
- Only include expensive setup (like parsing) outside the loop
519+
520+
3. **Package-Level Comment**
521+
- Add this comment at the top of each benchmark file:
522+
```go
523+
// Note on b.Fatalf usage in benchmarks:
524+
// Using b.Fatalf for errors in benchmark setup or execution is an acceptable pattern.
525+
// These operations (parse, diff, validate, etc.) should never fail with valid test fixtures.
526+
// If they do fail, it indicates a bug that should halt the benchmark immediately.
527+
```
528+
529+
4. **Consistency Across Packages**
530+
- All benchmark files (`parser`, `validator`, `converter`, `joiner`, `differ`) use the same pattern
531+
- This ensures maintainability and consistency with Go evolution
532+
533+
**Why This Matters**:
534+
- **Consistency**: All existing benchmarks use `for b.Loop()`
535+
- **Best Practice**: Modern Go 1.24+ approach
536+
- **Automatic Handling**: `b.Loop()` manages allocations reporting and timing
537+
- **Future Compatibility**: Aligns with Go's benchmark evolution
538+
471539
## Submitting changes
472540

473541
**Before Submitting Code:**

Makefile

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build test lint clean install tidy check help bench bench-parser bench-validator bench-converter bench-joiner bench-save bench-compare bench-baseline bench-clean release-test release-clean
1+
.PHONY: build test lint clean install tidy check help bench bench-parser bench-validator bench-converter bench-joiner bench-differ bench-save bench-compare bench-baseline bench-clean release-test release-clean
22

33
# Build variables
44
BINARY_NAME=oastools
@@ -83,7 +83,7 @@ check: tidy fmt lint test
8383
## bench: Run all benchmarks
8484
bench:
8585
@echo "Running all benchmarks ($(BENCH_TIME) per benchmark)..."
86-
@go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./parser ./validator ./converter ./joiner
86+
@go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./parser ./validator ./converter ./joiner ./differ
8787

8888
## bench-parser: Run parser benchmarks only
8989
bench-parser:
@@ -105,19 +105,24 @@ bench-joiner:
105105
@echo "Running joiner benchmarks..."
106106
@go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./joiner
107107

108+
## bench-differ: Run differ benchmarks only
109+
bench-differ:
110+
@echo "Running differ benchmarks..."
111+
@go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./differ
112+
108113
## bench-save: Run all benchmarks and save to timestamped file
109114
bench-save:
110115
@echo "Running benchmarks and saving results..."
111116
@TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \
112117
OUTPUT_FILE="benchmark-$${TIMESTAMP}.txt"; \
113-
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./parser ./validator ./converter ./joiner 2>&1 | tee "$${OUTPUT_FILE}"; \
118+
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./parser ./validator ./converter ./joiner ./differ 2>&1 | tee "$${OUTPUT_FILE}"; \
114119
echo ""; \
115120
echo "Benchmark results saved to: $${OUTPUT_FILE}"
116121

117122
## bench-baseline: Run benchmarks and update baseline file
118123
bench-baseline:
119124
@echo "Running benchmarks and updating baseline..."
120-
@go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./parser ./validator ./converter ./joiner 2>&1 | tee benchmark-baseline.txt
125+
@go test -bench=. -benchmem -benchtime=$(BENCH_TIME) ./parser ./validator ./converter ./joiner ./differ 2>&1 | tee benchmark-baseline.txt
121126
@echo ""
122127
@echo "Baseline updated: benchmark-baseline.txt"
123128

@@ -145,7 +150,7 @@ bench-cpu:
145150
@echo "Running benchmarks with CPU profiling..."
146151
@TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \
147152
PROFILE_FILE="cpu-profile-$${TIMESTAMP}.prof"; \
148-
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) -cpuprofile="$${PROFILE_FILE}" ./parser ./validator ./converter ./joiner; \
153+
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) -cpuprofile="$${PROFILE_FILE}" ./parser ./validator ./converter ./joiner ./differ; \
149154
echo ""; \
150155
echo "CPU profile saved to: $${PROFILE_FILE}"; \
151156
echo "Analyze with: go tool pprof $${PROFILE_FILE}"
@@ -155,7 +160,7 @@ bench-mem:
155160
@echo "Running benchmarks with memory profiling..."
156161
@TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \
157162
PROFILE_FILE="mem-profile-$${TIMESTAMP}.prof"; \
158-
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) -memprofile="$${PROFILE_FILE}" ./parser ./validator ./converter ./joiner; \
163+
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) -memprofile="$${PROFILE_FILE}" ./parser ./validator ./converter ./joiner ./differ; \
159164
echo ""; \
160165
echo "Memory profile saved to: $${PROFILE_FILE}"; \
161166
echo "Analyze with: go tool pprof $${PROFILE_FILE}"
@@ -166,7 +171,7 @@ bench-profile:
166171
@TIMESTAMP=$$(date +%Y%m%d-%H%M%S); \
167172
CPU_PROFILE="cpu-profile-$${TIMESTAMP}.prof"; \
168173
MEM_PROFILE="mem-profile-$${TIMESTAMP}.prof"; \
169-
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) -cpuprofile="$${CPU_PROFILE}" -memprofile="$${MEM_PROFILE}" ./parser ./validator ./converter ./joiner; \
174+
go test -bench=. -benchmem -benchtime=$(BENCH_TIME) -cpuprofile="$${CPU_PROFILE}" -memprofile="$${MEM_PROFILE}" ./parser ./validator ./converter ./joiner ./differ; \
170175
echo ""; \
171176
echo "CPU profile saved to: $${CPU_PROFILE}"; \
172177
echo "Memory profile saved to: $${MEM_PROFILE}"; \

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,15 @@ See [pkg.go.dev](https://pkg.go.dev/github.com/erraggy/oastools) for complete AP
166166

167167
## Benchmarks
168168

169-
The library includes comprehensive performance benchmarking (60+ benchmarks across all packages). As of v1.7.0, significant optimizations have been implemented:
169+
The library includes comprehensive performance benchmarking (70+ benchmarks across all packages). As of v1.9.1, significant optimizations have been implemented:
170170

171171
**Performance Highlights**:
172172
- **25-32% faster** JSON marshaling (v1.7.0 optimization)
173173
- **29-37% fewer** memory allocations
174174
- **30x faster** validation with `ValidateParsed` vs `Validate` (parse once, validate many)
175175
- **9x faster** conversion with `ConvertParsed` vs `Convert` (parse once, convert many)
176176
- **154x faster** joining with `JoinParsed` vs `Join` (parse once, join many)
177+
- **58x faster** diffing with `DiffParsed` vs `Diff` (parse once, diff many)
177178

178179
**Document Processing Performance** (Apple M4, Go 1.24):
179180

@@ -183,6 +184,7 @@ The library includes comprehensive performance benchmarking (60+ benchmarks acro
183184
| Validate | 143 μs | 1,160 μs | 14,635 μs |
184185
| Convert (OAS2→3) | 153 μs | 1,314 μs | - |
185186
| Join (2 docs) | 115 μs | - | - |
187+
| Diff (2 docs) | 457 μs | - | - |
186188

187189
For detailed performance analysis, methodology, and optimization strategies, see [benchmarks.md](benchmarks.md).
188190

benchmarks.md

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This document provides detailed performance analysis and benchmark results for t
44

55
## Overview
66

7-
As of v1.7.0, oastools includes comprehensive performance benchmarking infrastructure covering all major operations across the parser, validator, converter, and joiner packages. The library has undergone targeted optimizations to achieve significant performance improvements while maintaining correctness and code quality.
7+
As of v1.9.1, oastools includes comprehensive performance benchmarking infrastructure covering all major operations across the parser, validator, converter, joiner, and differ packages. The library has undergone targeted optimizations to achieve significant performance improvements while maintaining correctness and code quality.
88

99
**Platform**: Apple M4, darwin/arm64, Go 1.24
1010

@@ -35,7 +35,7 @@ The v1.7.0 release includes a major optimization to JSON marshaling that elimina
3535

3636
## Benchmark Suite
3737

38-
The benchmark suite includes **60+ benchmarks** across four main packages:
38+
The benchmark suite includes **70+ benchmarks** across five main packages:
3939

4040
### Parser Package (32 benchmarks)
4141

@@ -84,6 +84,16 @@ The benchmark suite includes **60+ benchmarks** across four main packages:
8484
- Array merge strategies
8585
- Tag deduplication
8686

87+
### Differ Package (10 benchmarks)
88+
89+
**Diffing Operations**:
90+
- Diff (parse + diff) vs DiffParsed (pre-parsed)
91+
- Simple mode vs Breaking mode
92+
- Convenience functions (Diff, DiffParsed) vs struct-based API
93+
- Configuration options (IncludeInfo)
94+
- Edge cases (identical specifications)
95+
- Parse-once pattern efficiency
96+
8797
## Current Performance Metrics
8898

8999
### Parser Performance
@@ -178,16 +188,43 @@ The benchmark suite includes **60+ benchmarks** across four main packages:
178188

179189
**Observation**: JoinParsed is **154x faster** than Join for 2 small documents (747ns vs 115μs) because it skips parsing. The actual joining operation has minimal overhead.
180190

191+
### Differ Performance
192+
193+
**Diffing** (parse + diff):
194+
195+
| Mode | Time (μs) | Memory (KB) | Allocations |
196+
|----------|-----------|-------------|-------------|
197+
| Simple | 457 | 609 | 7,156 |
198+
| Breaking | 458 | 611 | 7,157 |
199+
200+
**Diffing pre-parsed documents** (DiffParsed):
201+
202+
| Mode | Time (μs) | Memory (KB) | Allocations |
203+
|----------|-----------|-------------|-------------|
204+
| Simple | 7.82 | 6.89 | 138 |
205+
| Breaking | 7.97 | 6.96 | 137 |
206+
207+
**Special cases**:
208+
209+
| Case | Time (μs) | Memory (KB) | Allocations |
210+
|--------------------|-----------|-------------|-------------|
211+
| Identical specs | 2.82 | 2.08 | 79 |
212+
| With info changes | 7.97 | 6.96 | 137 |
213+
| Without info | 8.10 | 8.11 | 138 |
214+
215+
**Observation**: DiffParsed is **58x faster** than Diff (7.8μs vs 457μs) because it skips parsing. The differ includes a fast path for identical specifications that runs in ~2.8μs. Breaking mode vs Simple mode has negligible performance difference (~0.15μs), making breaking change detection essentially free.
216+
181217
## Performance Best Practices
182218

183219
### For Library Users
184220

185-
1. **Reuse parser/validator/converter/joiner instances** when processing multiple files with the same configuration
186-
2. **Use ParseOnce workflows**: For operations on the same document (validate, convert, join), parse once and pass the `ParseResult` to subsequent operations:
221+
1. **Reuse parser/validator/converter/joiner/differ instances** when processing multiple files with the same configuration
222+
2. **Use ParseOnce workflows**: For operations on the same document (validate, convert, join, diff), parse once and pass the `ParseResult` to subsequent operations:
187223
```go
188224
result, _ := parser.Parse("spec.yaml", false, true)
189225
validator.ValidateParsed(result, true, false) // 30x faster than Validate
190226
converter.ConvertParsed(result, "3.0.3") // 9x faster than Convert
227+
differ.DiffParsed(result1, result2) // 58x faster than Diff
191228
```
192229
3. **Disable reference resolution** (`ResolveRefs=false`) when not needed
193230
4. **Disable validation** during parsing (`ValidateStructure=false`) if you'll validate separately
@@ -220,12 +257,14 @@ make bench-parser
220257
make bench-validator
221258
make bench-converter
222259
make bench-joiner
260+
make bench-differ
223261

224262
# Or individually
225263
go test -bench=. -benchmem ./parser
226264
go test -bench=. -benchmem ./validator
227265
go test -bench=. -benchmem ./converter
228266
go test -bench=. -benchmem ./joiner
267+
go test -bench=. -benchmem ./differ
229268

230269
# Save baseline for comparison
231270
go test -bench=. -benchmem ./... > benchmark-baseline.txt

differ/bench-results.txt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
goos: darwin
2+
goarch: arm64
3+
pkg: github.com/erraggy/oastools/differ
4+
cpu: Apple M4
5+
BenchmarkDiffConvenience-10 2523 436911 ns/op 607112 B/op 7156 allocs/op
6+
BenchmarkDiffConvenience-10 2776 446545 ns/op 607657 B/op 7156 allocs/op
7+
BenchmarkDiffConvenience-10 2698 439833 ns/op 607993 B/op 7156 allocs/op
8+
BenchmarkDiffConvenience-10 2757 508917 ns/op 608323 B/op 7156 allocs/op
9+
BenchmarkDiffConvenience-10 2715 451005 ns/op 608371 B/op 7156 allocs/op
10+
BenchmarkDiffParsedConvenience-10 156670 7678 ns/op 6891 B/op 138 allocs/op
11+
BenchmarkDiffParsedConvenience-10 157692 7662 ns/op 6891 B/op 138 allocs/op
12+
BenchmarkDiffParsedConvenience-10 156468 7670 ns/op 6891 B/op 138 allocs/op
13+
BenchmarkDiffParsedConvenience-10 156780 7728 ns/op 6891 B/op 138 allocs/op
14+
BenchmarkDiffParsedConvenience-10 155041 7691 ns/op 6891 B/op 138 allocs/op
15+
BenchmarkDifferDiff-10 2600 452266 ns/op 609324 B/op 7156 allocs/op
16+
BenchmarkDifferDiff-10 2640 455860 ns/op 610072 B/op 7157 allocs/op
17+
BenchmarkDifferDiff-10 2637 457806 ns/op 610558 B/op 7157 allocs/op
18+
BenchmarkDifferDiff-10 2600 460511 ns/op 610688 B/op 7157 allocs/op
19+
BenchmarkDifferDiff-10 2638 458362 ns/op 610756 B/op 7157 allocs/op
20+
BenchmarkDifferDiffParsed-10 153064 7812 ns/op 6891 B/op 138 allocs/op
21+
BenchmarkDifferDiffParsed-10 153217 7847 ns/op 6891 B/op 138 allocs/op
22+
BenchmarkDifferDiffParsed-10 154752 7768 ns/op 6891 B/op 138 allocs/op
23+
BenchmarkDifferDiffParsed-10 153975 7823 ns/op 6891 B/op 138 allocs/op
24+
BenchmarkDifferDiffParsed-10 153349 7807 ns/op 6891 B/op 138 allocs/op
25+
BenchmarkDifferSimpleMode-10 149916 7820 ns/op 6891 B/op 138 allocs/op
26+
BenchmarkDifferSimpleMode-10 153992 7799 ns/op 6891 B/op 138 allocs/op
27+
BenchmarkDifferSimpleMode-10 154177 7809 ns/op 6891 B/op 138 allocs/op
28+
BenchmarkDifferSimpleMode-10 153376 7836 ns/op 6891 B/op 138 allocs/op
29+
BenchmarkDifferSimpleMode-10 151728 7872 ns/op 6891 B/op 138 allocs/op
30+
BenchmarkDifferBreakingMode-10 150068 7931 ns/op 6955 B/op 137 allocs/op
31+
BenchmarkDifferBreakingMode-10 150386 8020 ns/op 6955 B/op 137 allocs/op
32+
BenchmarkDifferBreakingMode-10 149853 7989 ns/op 6955 B/op 137 allocs/op
33+
BenchmarkDifferBreakingMode-10 151822 7969 ns/op 6955 B/op 137 allocs/op
34+
BenchmarkDifferBreakingMode-10 151111 7944 ns/op 6955 B/op 137 allocs/op
35+
BenchmarkDifferWithInfo-10 150004 7974 ns/op 6955 B/op 137 allocs/op
36+
BenchmarkDifferWithInfo-10 148920 7946 ns/op 6955 B/op 137 allocs/op
37+
BenchmarkDifferWithInfo-10 150672 7988 ns/op 6955 B/op 137 allocs/op
38+
BenchmarkDifferWithInfo-10 150805 7965 ns/op 6955 B/op 137 allocs/op
39+
BenchmarkDifferWithInfo-10 151230 7977 ns/op 6955 B/op 137 allocs/op
40+
BenchmarkDifferWithoutInfo-10 148821 8086 ns/op 8108 B/op 138 allocs/op
41+
BenchmarkDifferWithoutInfo-10 148964 8108 ns/op 8108 B/op 138 allocs/op
42+
BenchmarkDifferWithoutInfo-10 147596 8111 ns/op 8108 B/op 138 allocs/op
43+
BenchmarkDifferWithoutInfo-10 148171 8091 ns/op 8108 B/op 138 allocs/op
44+
BenchmarkDifferWithoutInfo-10 147568 8129 ns/op 8108 B/op 138 allocs/op
45+
BenchmarkDifferIdenticalSpecs-10 425127 2829 ns/op 2081 B/op 79 allocs/op
46+
BenchmarkDifferIdenticalSpecs-10 418862 2822 ns/op 2081 B/op 79 allocs/op
47+
BenchmarkDifferIdenticalSpecs-10 429141 2829 ns/op 2081 B/op 79 allocs/op
48+
BenchmarkDifferIdenticalSpecs-10 426162 2821 ns/op 2081 B/op 79 allocs/op
49+
BenchmarkDifferIdenticalSpecs-10 421341 2827 ns/op 2081 B/op 79 allocs/op
50+
BenchmarkParseOnceDiffMany-10 151274 7975 ns/op 6955 B/op 137 allocs/op
51+
BenchmarkParseOnceDiffMany-10 150036 7994 ns/op 6955 B/op 137 allocs/op
52+
BenchmarkParseOnceDiffMany-10 149776 7988 ns/op 6955 B/op 137 allocs/op
53+
BenchmarkParseOnceDiffMany-10 149809 7984 ns/op 6955 B/op 137 allocs/op
54+
BenchmarkParseOnceDiffMany-10 149707 8006 ns/op 6955 B/op 137 allocs/op
55+
PASS
56+
ok github.com/erraggy/oastools/differ 64.034s

0 commit comments

Comments
 (0)