mirror of
https://github.com/jimeh/go-mocktesting.git
synced 2026-02-19 03:46:40 +00:00
feat: Add Go 1.24 Context and Chdir methods
Co-authored-by: cursor.nop <cursor.nop@jimeh.info>
This commit is contained in:
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: "1.24"
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: "1.24"
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
@@ -71,6 +71,13 @@ jobs:
|
||||
- "1.15"
|
||||
- "1.16"
|
||||
- "1.17"
|
||||
- "1.18"
|
||||
- "1.19"
|
||||
- "1.20"
|
||||
- "1.21"
|
||||
- "1.22"
|
||||
- "1.23"
|
||||
- "1.24"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Features
|
||||
|
||||
* **testing:** Add support for Go 1.24 Context() and Chdir() methods
|
||||
* **ci:** Test against Go 1.18-1.24 in addition to existing versions
|
||||
|
||||
## 0.1.0 (2021-11-22)
|
||||
|
||||
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
# Go Version Support Plan for go-mocktesting
|
||||
|
||||
## Research Summary
|
||||
|
||||
### Current State
|
||||
- **Base Go Version**: 1.15 (from go.mod)
|
||||
- **Currently Supported**: Go 1.15, 1.16 (with Setenv method)
|
||||
- **Latest Go Version**: 1.24
|
||||
- **Missing Methods**: Context(), Chdir()
|
||||
|
||||
### Complete testing.T Method Inventory
|
||||
|
||||
All public methods on *testing.T as of Go 1.24:
|
||||
- Chdir ❌ (Go 1.24) - **MISSING**
|
||||
- Cleanup ✅
|
||||
- Context ❌ (Go 1.24) - **MISSING**
|
||||
- Deadline ✅
|
||||
- Error ✅
|
||||
- Errorf ✅
|
||||
- Fail ✅
|
||||
- FailNow ✅
|
||||
- Failed ✅
|
||||
- Fatal ✅
|
||||
- Fatalf ✅
|
||||
- Helper ✅
|
||||
- Log ✅
|
||||
- Logf ✅
|
||||
- Name ✅
|
||||
- Parallel ✅
|
||||
- Run ✅
|
||||
- Setenv ✅ (Go 1.16+)
|
||||
- Skip ✅
|
||||
- SkipNow ✅
|
||||
- Skipf ✅
|
||||
- Skipped ✅
|
||||
- TempDir ✅
|
||||
|
||||
## Version-by-Version Analysis
|
||||
|
||||
### Go 1.15 (Base)
|
||||
**File**: `t.go`
|
||||
**Status**: ✅ Fully implemented
|
||||
|
||||
Methods implemented:
|
||||
- Core test methods (Fail, FailNow, Failed, Error, Errorf, Fatal, Fatalf)
|
||||
- Logging (Log, Logf)
|
||||
- Skipping (Skip, Skipf, SkipNow, Skipped)
|
||||
- Helpers (Helper)
|
||||
- Test organization (Name, Parallel, Run, Deadline)
|
||||
- Cleanup (Cleanup)
|
||||
- Temporary directories (TempDir)
|
||||
|
||||
### Go 1.16
|
||||
**File**: `t_go116.go` (with build tag `//go:build go1.16`)
|
||||
**Status**: ✅ Fully implemented
|
||||
|
||||
New methods:
|
||||
- `Setenv(key, value string)` - Set environment variable for test
|
||||
|
||||
### Go 1.17
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.18
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.19
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.20
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.21
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.22
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.23
|
||||
**Status**: ✅ No new testing.T methods
|
||||
|
||||
### Go 1.24
|
||||
**File**: `t_go124.go` (needs to be created)
|
||||
**Status**: ❌ Not implemented
|
||||
|
||||
New methods:
|
||||
1. **Context() context.Context**
|
||||
- Returns a context.Context that is canceled just before
|
||||
Cleanup-registered functions are called
|
||||
- Allows cleanup functions to wait for resources that shut down
|
||||
on Context.Done before the test completes
|
||||
|
||||
2. **Chdir(dir string)**
|
||||
- Calls os.Chdir(dir) and uses Cleanup to restore the current
|
||||
working directory after the test
|
||||
- On Unix, also sets PWD environment variable for the duration
|
||||
- Cannot be used in parallel tests (affects whole process)
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Add Go 1.24 Support ⏳
|
||||
|
||||
#### Step 1.1: Create `t_go124.go`
|
||||
- Add build constraint: `//go:build go1.24`
|
||||
- Implement `Context() context.Context` method
|
||||
- Store context in T struct (add ctx field)
|
||||
- Create context on T initialization
|
||||
- Cancel context before cleanup functions run
|
||||
- Return the context
|
||||
- Implement `Chdir(dir string)` method
|
||||
- Call os.Chdir(dir)
|
||||
- Register cleanup to restore original directory
|
||||
- Store original PWD and restore it
|
||||
- Track current directory changes
|
||||
|
||||
#### Step 1.2: Update T struct in `t.go`
|
||||
- Add `ctx context.Context` field
|
||||
- Add `ctxCancel context.CancelFunc` field
|
||||
- Add `origDir string` field for Chdir tracking
|
||||
- Initialize context in NewT()
|
||||
|
||||
#### Step 1.3: Create `t_go124_test.go`
|
||||
- Add build constraint: `//go:build go1.16`
|
||||
- Test Context() method:
|
||||
- Verify context is not nil
|
||||
- Verify context is canceled during cleanup
|
||||
- Test that cleanup can use Context.Done
|
||||
- Test Chdir() method:
|
||||
- Verify directory changes work
|
||||
- Verify directory is restored after test
|
||||
- Verify PWD environment variable handling
|
||||
- Verify panic/error on parallel tests
|
||||
|
||||
#### Step 1.4: Update TestT_methods test
|
||||
- Ensure it runs on all Go versions
|
||||
- Verify no failures for Go 1.24+
|
||||
|
||||
### Phase 2: Update CI Configuration ⏳
|
||||
|
||||
#### Step 2.1: Update `.github/workflows/ci.yml`
|
||||
Current test matrix:
|
||||
```yaml
|
||||
go_version:
|
||||
- "1.15"
|
||||
- "1.16"
|
||||
- "1.17"
|
||||
```
|
||||
|
||||
Add newer versions:
|
||||
```yaml
|
||||
go_version:
|
||||
- "1.15"
|
||||
- "1.16"
|
||||
- "1.17"
|
||||
- "1.18"
|
||||
- "1.19"
|
||||
- "1.20"
|
||||
- "1.21"
|
||||
- "1.22"
|
||||
- "1.23"
|
||||
- "1.24"
|
||||
```
|
||||
|
||||
#### Step 2.2: Update other CI jobs
|
||||
- Update `lint` job to use Go 1.24
|
||||
- Update `tidy` job to use Go 1.24
|
||||
- Update `cov` job to use Go 1.24
|
||||
|
||||
### Phase 3: Documentation Updates ⏳
|
||||
|
||||
#### Step 3.1: Update README.md
|
||||
- Note Go 1.24 support
|
||||
- Document new Context() and Chdir() methods
|
||||
- Add examples if appropriate
|
||||
|
||||
#### Step 3.2: Update CHANGELOG.md
|
||||
Add new version entry:
|
||||
```markdown
|
||||
## [Next Release]
|
||||
|
||||
### Features
|
||||
|
||||
* **testing:** Add support for Go 1.24 Context() and Chdir() methods
|
||||
* **ci:** Test against Go 1.18-1.24
|
||||
```
|
||||
|
||||
#### Step 3.3: Update go.mod
|
||||
Consider whether to bump minimum version or keep at 1.15
|
||||
|
||||
### Phase 4: Testing & Validation ⏳
|
||||
|
||||
#### Step 4.1: Run TestT_methods
|
||||
```bash
|
||||
go test -v -run TestT_methods
|
||||
```
|
||||
Should pass with no failures.
|
||||
|
||||
#### Step 4.2: Run full test suite on all versions
|
||||
```bash
|
||||
for v in 1.15 1.16 1.17 1.18 1.19 1.20 1.21 1.22 1.23 1.24; do
|
||||
go$v test -v -race ./...
|
||||
done
|
||||
```
|
||||
|
||||
#### Step 4.3: Verify coverage
|
||||
```bash
|
||||
make cov
|
||||
```
|
||||
Ensure new methods are covered.
|
||||
|
||||
## Implementation Details for Go 1.24 Methods
|
||||
|
||||
### Context() Implementation
|
||||
|
||||
```go
|
||||
//go:build go1.24
|
||||
// +build go1.24
|
||||
|
||||
package mocktesting
|
||||
|
||||
import "context"
|
||||
|
||||
// Context returns a context that is canceled just before Cleanup-registered
|
||||
// functions are called.
|
||||
func (t *T) Context() context.Context {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.ctx
|
||||
}
|
||||
```
|
||||
|
||||
Required struct changes:
|
||||
```go
|
||||
type T struct {
|
||||
// ... existing fields ...
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
}
|
||||
```
|
||||
|
||||
Initialize in NewT():
|
||||
```go
|
||||
func NewT(name string, options ...Option) *T {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t := &T{
|
||||
// ... existing fields ...
|
||||
ctx: ctx,
|
||||
ctxCancel: cancel,
|
||||
}
|
||||
// ... rest of init ...
|
||||
}
|
||||
```
|
||||
|
||||
### Chdir() Implementation
|
||||
|
||||
```go
|
||||
//go:build go1.24
|
||||
// +build go1.24
|
||||
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Chdir calls os.Chdir(dir) and uses Cleanup to restore the current
|
||||
// working directory to its original value after the test.
|
||||
func (t *T) Chdir(dir string) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
// Store original directory on first call
|
||||
if t.origDir == "" {
|
||||
origDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.internalError(err)
|
||||
return
|
||||
}
|
||||
t.origDir = origDir
|
||||
|
||||
// Register cleanup to restore directory
|
||||
t.Cleanup(func() {
|
||||
os.Chdir(t.origDir)
|
||||
})
|
||||
}
|
||||
|
||||
// Change directory
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
t.internalError(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Required struct changes:
|
||||
```go
|
||||
type T struct {
|
||||
// ... existing fields ...
|
||||
origDir string
|
||||
}
|
||||
```
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### Build Tags
|
||||
Use `//go:build go1.24` syntax (not older `// +build` only) for
|
||||
consistency with modern Go.
|
||||
|
||||
### Context Lifecycle
|
||||
- Context is created at T initialization
|
||||
- Context is NOT canceled during test execution
|
||||
- Context IS canceled before Cleanup functions run
|
||||
- This allows cleanup functions to wait on Context.Done
|
||||
|
||||
### Chdir Behavior
|
||||
- First call to Chdir stores original directory
|
||||
- Cleanup is registered once to restore directory
|
||||
- Subsequent calls change directory but don't add more cleanups
|
||||
- Errors in Chdir call internalError (consistent with TempDir)
|
||||
|
||||
### Backward Compatibility
|
||||
- Keep base version at Go 1.15 in go.mod
|
||||
- Use build tags to conditionally compile version-specific methods
|
||||
- All existing code continues to work on Go 1.15+
|
||||
- New methods only available when compiled with Go 1.24+
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
Each new method needs comprehensive unit tests:
|
||||
- Context():
|
||||
- Returns non-nil context
|
||||
- Context is canceled before cleanup
|
||||
- Can be used in cleanup functions
|
||||
- Works with subtests
|
||||
- Chdir():
|
||||
- Directory changes work
|
||||
- Directory is restored
|
||||
- Multiple calls work correctly
|
||||
- Error handling
|
||||
|
||||
### Integration Tests
|
||||
- Verify TestT_methods passes (the reflective test)
|
||||
- Test interaction with existing methods
|
||||
- Test with subtests (Run())
|
||||
- Test error conditions
|
||||
|
||||
### CI Testing
|
||||
- Test on all Go versions 1.15-1.24
|
||||
- Test on multiple OS (Linux, macOS, Windows)
|
||||
- Run with race detector
|
||||
- Verify coverage
|
||||
|
||||
## Timeline Estimate
|
||||
|
||||
- Phase 1 (Implementation): 2-3 hours
|
||||
- Phase 2 (CI Updates): 30 minutes
|
||||
- Phase 3 (Documentation): 30 minutes
|
||||
- Phase 4 (Testing): 1 hour
|
||||
- **Total**: ~4-5 hours
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
### Low Risk
|
||||
- Go 1.17-1.23 have no new methods (already compatible)
|
||||
- Implementation pattern established with Go 1.16 (Setenv)
|
||||
- Good test coverage with TestT_methods
|
||||
|
||||
### Medium Risk
|
||||
- Context lifecycle management needs careful implementation
|
||||
- Chdir affects process state (need proper cleanup)
|
||||
- CI configuration changes need validation
|
||||
|
||||
### Mitigation
|
||||
- Follow established patterns from Setenv implementation
|
||||
- Write comprehensive tests before implementation
|
||||
- Test on multiple Go versions locally before CI
|
||||
- Use build tags to isolate version-specific code
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ TestT_methods passes on Go 1.24
|
||||
2. ✅ All existing tests pass on Go 1.15-1.24
|
||||
3. ✅ New methods have >90% test coverage
|
||||
4. ✅ CI tests pass on all versions
|
||||
5. ✅ Documentation is updated
|
||||
6. ✅ No breaking changes to existing API
|
||||
|
||||
## Notes
|
||||
|
||||
- The test failure currently shows: "Chdir" and "Context" are not
|
||||
implemented
|
||||
- These are the ONLY two methods missing
|
||||
- Go 1.17-1.23 are already fully supported (no new methods)
|
||||
- Implementation should be straightforward following existing patterns
|
||||
34
t.go
34
t.go
@@ -1,6 +1,7 @@
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -40,17 +41,20 @@ type T struct {
|
||||
timeout bool
|
||||
|
||||
// State - Fields which record how T has been modified via method calls.
|
||||
mux sync.RWMutex
|
||||
skipped bool
|
||||
failed int
|
||||
parallel bool
|
||||
output []string
|
||||
helpers []string
|
||||
aborted bool
|
||||
cleanups []func()
|
||||
env map[string]string
|
||||
subtests []*T
|
||||
tempdirs []string
|
||||
mux sync.RWMutex
|
||||
skipped bool
|
||||
failed int
|
||||
parallel bool
|
||||
output []string
|
||||
helpers []string
|
||||
aborted bool
|
||||
cleanups []func()
|
||||
env map[string]string
|
||||
subtests []*T
|
||||
tempdirs []string
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
origDir string
|
||||
|
||||
// subtestNames is used to ensure subtests do not have conflicting names.
|
||||
subtestNames map[string]bool
|
||||
@@ -70,12 +74,15 @@ type T struct {
|
||||
var _ testing.TB = (*T)(nil)
|
||||
|
||||
func NewT(name string, options ...Option) *T {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t := &T{
|
||||
name: strings.ReplaceAll(name, " ", "_"),
|
||||
abort: true,
|
||||
baseTempdir: os.TempDir(),
|
||||
deadline: time.Now().Add(10 * time.Minute),
|
||||
timeout: true,
|
||||
ctx: ctx,
|
||||
ctxCancel: cancel,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
@@ -415,6 +422,11 @@ func (t *T) Run(name string, f func(testing.TB)) bool {
|
||||
|
||||
Go(func() {
|
||||
f(subtest)
|
||||
// Cancel subtest's context after test function completes
|
||||
// but before cleanup functions run
|
||||
if subtest.ctxCancel != nil {
|
||||
subtest.ctxCancel()
|
||||
}
|
||||
})
|
||||
|
||||
if subtest.Failed() {
|
||||
|
||||
69
t_go124.go
Normal file
69
t_go124.go
Normal file
@@ -0,0 +1,69 @@
|
||||
//go:build go1.24
|
||||
// +build go1.24
|
||||
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Context returns a context that is canceled just before Cleanup-registered
|
||||
// functions are called.
|
||||
//
|
||||
// Cleanup functions can wait for any resources that shut down on
|
||||
// Context.Done before the test or benchmark completes.
|
||||
func (t *T) Context() context.Context {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.ctx
|
||||
}
|
||||
|
||||
// Chdir calls os.Chdir(dir) and uses Cleanup to restore the current
|
||||
// working directory to its original value after the test. On Unix, it
|
||||
// also sets PWD environment variable for the duration of the test.
|
||||
//
|
||||
// Because Chdir affects the whole process, it cannot be used in parallel
|
||||
// tests or tests with parallel ancestors.
|
||||
func (t *T) Chdir(dir string) {
|
||||
// Store original directory on first call
|
||||
t.mux.Lock()
|
||||
needsCleanup := t.origDir == ""
|
||||
if needsCleanup {
|
||||
origDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.mux.Unlock()
|
||||
err = os.ErrInvalid
|
||||
t.internalError(err)
|
||||
return
|
||||
}
|
||||
t.origDir = origDir
|
||||
}
|
||||
t.mux.Unlock()
|
||||
|
||||
// Register cleanup outside of lock (Cleanup acquires lock)
|
||||
if needsCleanup {
|
||||
origDir := t.origDir
|
||||
t.Cleanup(func() {
|
||||
_ = os.Chdir(origDir)
|
||||
})
|
||||
}
|
||||
|
||||
// Change directory
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
t.internalError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// OrigDir returns the original working directory when the test was
|
||||
// created, or the empty string if Chdir has not been called yet.
|
||||
//
|
||||
// This is a helper method specific to *mocktesting.T to allow inspection
|
||||
// of the original directory in tests.
|
||||
func (t *T) OrigDir() string {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
return t.origDir
|
||||
}
|
||||
339
t_go124_test.go
Normal file
339
t_go124_test.go
Normal file
@@ -0,0 +1,339 @@
|
||||
//go:build go1.24
|
||||
// +build go1.24
|
||||
|
||||
package mocktesting
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestT_Context(t *testing.T) {
|
||||
t.Run("returns non-nil context", func(t *testing.T) {
|
||||
mt := NewT("TestContext")
|
||||
|
||||
ctx := mt.Context()
|
||||
|
||||
assert.NotNil(t, ctx)
|
||||
})
|
||||
|
||||
t.Run("context is not canceled initially", func(t *testing.T) {
|
||||
mt := NewT("TestContext")
|
||||
|
||||
ctx := mt.Context()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("context should not be canceled initially")
|
||||
default:
|
||||
// Expected
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("context is canceled after test completes", func(t *testing.T) {
|
||||
mt := NewT("TestContext")
|
||||
var ctx context.Context
|
||||
|
||||
mt.Run("subtest", func(t testing.TB) {
|
||||
subMt := t.(*T)
|
||||
ctx = subMt.Context()
|
||||
})
|
||||
|
||||
// After subtest completes, its context should be canceled
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Expected
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatal("context should be canceled after test completes")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("cleanup can wait on context", func(t *testing.T) {
|
||||
mt := NewT("TestContext")
|
||||
var subCtx context.Context
|
||||
|
||||
mt.Run("subtest", func(t testing.TB) {
|
||||
subMt := t.(*T)
|
||||
subCtx = subMt.Context()
|
||||
|
||||
// Add a cleanup function
|
||||
subMt.Cleanup(func() {
|
||||
// This will be recorded but not run automatically
|
||||
})
|
||||
})
|
||||
|
||||
// After subtest completes, context should be canceled
|
||||
select {
|
||||
case <-subCtx.Done():
|
||||
// Expected - context is canceled
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatal("context should be canceled after subtest completes")
|
||||
}
|
||||
|
||||
// Verify cleanup was registered
|
||||
subtests := mt.Subtests()
|
||||
assert.Len(t, subtests, 1)
|
||||
assert.Len(t, subtests[0].CleanupFuncs(), 1,
|
||||
"cleanup should be registered")
|
||||
})
|
||||
|
||||
t.Run("same context returned on multiple calls", func(t *testing.T) {
|
||||
mt := NewT("TestContext")
|
||||
|
||||
ctx1 := mt.Context()
|
||||
ctx2 := mt.Context()
|
||||
|
||||
assert.Same(t, ctx1, ctx2)
|
||||
})
|
||||
|
||||
t.Run("subtests have their own contexts", func(t *testing.T) {
|
||||
mt := NewT("TestContext")
|
||||
var ctx1, ctx2 context.Context
|
||||
|
||||
mt.Run("subtest1", func(t testing.TB) {
|
||||
ctx1 = t.(*T).Context()
|
||||
})
|
||||
|
||||
mt.Run("subtest2", func(t testing.TB) {
|
||||
ctx2 = t.(*T).Context()
|
||||
})
|
||||
|
||||
assert.NotSame(t, ctx1, ctx2,
|
||||
"each subtest should have its own context")
|
||||
})
|
||||
}
|
||||
|
||||
func TestT_Chdir(t *testing.T) {
|
||||
t.Run("changes directory", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
mt := NewT("TestChdir")
|
||||
|
||||
runInGoroutine(func() {
|
||||
mt.Chdir(tempDir)
|
||||
})
|
||||
|
||||
newDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tempDir, newDir)
|
||||
|
||||
// Cleanup
|
||||
os.Chdir(origDir)
|
||||
})
|
||||
|
||||
t.Run("stores original directory", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
mt := NewT("TestChdir")
|
||||
|
||||
runInGoroutine(func() {
|
||||
mt.Chdir(tempDir)
|
||||
})
|
||||
|
||||
assert.Equal(t, origDir, mt.OrigDir())
|
||||
|
||||
// Cleanup
|
||||
os.Chdir(origDir)
|
||||
})
|
||||
|
||||
t.Run("registers cleanup", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
mt := NewT("TestChdir")
|
||||
|
||||
runInGoroutine(func() {
|
||||
mt.Chdir(tempDir)
|
||||
})
|
||||
|
||||
cleanups := mt.CleanupFuncs()
|
||||
assert.Len(t, cleanups, 1,
|
||||
"Chdir should register one cleanup function")
|
||||
|
||||
// Cleanup
|
||||
os.Chdir(origDir)
|
||||
})
|
||||
|
||||
t.Run("multiple calls change directory", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
tempDir1 := t.TempDir()
|
||||
tempDir2 := filepath.Join(tempDir1, "subdir")
|
||||
require.NoError(t, os.Mkdir(tempDir2, 0755))
|
||||
|
||||
mt := NewT("TestChdir")
|
||||
|
||||
runInGoroutine(func() {
|
||||
mt.Chdir(tempDir1)
|
||||
dir1, _ := os.Getwd()
|
||||
assert.Equal(t, tempDir1, dir1)
|
||||
|
||||
mt.Chdir(tempDir2)
|
||||
dir2, _ := os.Getwd()
|
||||
assert.Equal(t, tempDir2, dir2)
|
||||
})
|
||||
|
||||
// Original dir should still be stored
|
||||
assert.Equal(t, origDir, mt.OrigDir())
|
||||
|
||||
// Should only have one cleanup (from first call)
|
||||
cleanups := mt.CleanupFuncs()
|
||||
assert.Len(t, cleanups, 1,
|
||||
"should only register cleanup once")
|
||||
|
||||
// Cleanup
|
||||
os.Chdir(origDir)
|
||||
})
|
||||
|
||||
t.Run("cleanup restores original directory", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
mt := NewT("TestChdir")
|
||||
|
||||
runInGoroutine(func() {
|
||||
mt.Chdir(tempDir)
|
||||
|
||||
// Verify we're in temp dir
|
||||
currentDir, _ := os.Getwd()
|
||||
assert.Equal(t, tempDir, currentDir)
|
||||
|
||||
// Run cleanup
|
||||
cleanups := mt.CleanupFuncs()
|
||||
for _, cleanup := range cleanups {
|
||||
cleanup()
|
||||
}
|
||||
})
|
||||
|
||||
// Verify we're back in original dir
|
||||
currentDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, origDir, currentDir)
|
||||
})
|
||||
|
||||
t.Run("error on invalid directory", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
mt := NewT("TestChdir", WithNoAbort())
|
||||
panicValue := ""
|
||||
|
||||
runInGoroutine(func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panicValue = r.(error).Error()
|
||||
}
|
||||
}()
|
||||
mt.Chdir("/nonexistent/directory/that/does/not/exist")
|
||||
})
|
||||
|
||||
assert.Contains(t, panicValue, "mocktesting:",
|
||||
"should panic with mocktesting error")
|
||||
})
|
||||
|
||||
t.Run("works with subtests", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
mt := NewT("TestChdir")
|
||||
|
||||
mt.Run("subtest", func(t testing.TB) {
|
||||
subMt := t.(*T)
|
||||
subMt.Chdir(tempDir)
|
||||
|
||||
currentDir, _ := os.Getwd()
|
||||
assert.Equal(t, tempDir, currentDir)
|
||||
})
|
||||
|
||||
// After subtest, should be back to original (if cleanup ran)
|
||||
// But since we're in the same process, we need to manually
|
||||
// restore for this test
|
||||
os.Chdir(origDir)
|
||||
})
|
||||
}
|
||||
|
||||
func TestT_Run_Go124(t *testing.T) {
|
||||
t.Run("subtest context is canceled after completion", func(t *testing.T) {
|
||||
mt := NewT("TestRun")
|
||||
var subCtx context.Context
|
||||
|
||||
mt.Run("subtest", func(t testing.TB) {
|
||||
subMt := t.(*T)
|
||||
subCtx = subMt.Context()
|
||||
})
|
||||
|
||||
// After subtest completes, context should be canceled
|
||||
select {
|
||||
case <-subCtx.Done():
|
||||
// Expected - context is canceled
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatal("subtest context should be canceled after completion")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("parent context not affected by subtest", func(t *testing.T) {
|
||||
mt := NewT("TestRun")
|
||||
parentCtx := mt.Context()
|
||||
|
||||
mt.Run("subtest", func(t testing.TB) {
|
||||
// Subtest runs and completes
|
||||
})
|
||||
|
||||
// Parent context should still be active
|
||||
select {
|
||||
case <-parentCtx.Done():
|
||||
t.Fatal("parent context should not be canceled")
|
||||
default:
|
||||
// Expected
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestT_OrigDir(t *testing.T) {
|
||||
t.Run("empty before Chdir called", func(t *testing.T) {
|
||||
mt := NewT("TestOrigDir")
|
||||
|
||||
origDir := mt.OrigDir()
|
||||
|
||||
assert.Empty(t, origDir)
|
||||
})
|
||||
|
||||
t.Run("set after Chdir called", func(t *testing.T) {
|
||||
origDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(origDir)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
mt := NewT("TestOrigDir")
|
||||
|
||||
runInGoroutine(func() {
|
||||
mt.Chdir(tempDir)
|
||||
})
|
||||
|
||||
storedOrigDir := mt.OrigDir()
|
||||
assert.Equal(t, origDir, storedOrigDir)
|
||||
|
||||
// Cleanup
|
||||
os.Chdir(origDir)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user