Improve code quality and thread safety (#844)
- Format Go files (gofmt) for backend/windows/notify*.go - Add thread safety to Stopwatch with sync.Mutex - Add comprehensive unit tests for Stopwatch with race detection - Add godoc comments to ImageCache public APIs - Improve IPC documentation with platform-specific socket path details - Document portable mode TODO for future enhancement
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStopwatch_Basic(t *testing.T) {
|
||||
sw := &Stopwatch{}
|
||||
|
||||
// Test initial state
|
||||
if elapsed := sw.Elapsed(); elapsed != 0 {
|
||||
t.Errorf("Expected initial elapsed time to be 0, got %v", elapsed)
|
||||
}
|
||||
|
||||
// Test start and elapsed
|
||||
sw.Start()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
elapsed := sw.Elapsed()
|
||||
if elapsed < 10*time.Millisecond {
|
||||
t.Errorf("Expected at least 10ms elapsed, got %v", elapsed)
|
||||
}
|
||||
|
||||
// Test stop
|
||||
sw.Stop()
|
||||
stoppedElapsed := sw.Elapsed()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
if sw.Elapsed() != stoppedElapsed {
|
||||
t.Error("Elapsed time should not increase after Stop()")
|
||||
}
|
||||
|
||||
// Test reset
|
||||
sw.Reset()
|
||||
if elapsed := sw.Elapsed(); elapsed != 0 {
|
||||
t.Errorf("Expected elapsed time to be 0 after reset, got %v", elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopwatch_StartStop(t *testing.T) {
|
||||
sw := &Stopwatch{}
|
||||
|
||||
// Start, accumulate some time
|
||||
sw.Start()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
sw.Stop()
|
||||
firstElapsed := sw.Elapsed()
|
||||
|
||||
// Start again, accumulate more time
|
||||
sw.Start()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
sw.Stop()
|
||||
secondElapsed := sw.Elapsed()
|
||||
|
||||
if secondElapsed <= firstElapsed {
|
||||
t.Errorf("Expected elapsed time to accumulate, first=%v second=%v", firstElapsed, secondElapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopwatch_DoubleStart(t *testing.T) {
|
||||
sw := &Stopwatch{}
|
||||
|
||||
sw.Start()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
firstStart := sw.Elapsed()
|
||||
|
||||
// Second Start() should be no-op
|
||||
sw.Start()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
secondStart := sw.Elapsed()
|
||||
|
||||
// Time should continue from first start
|
||||
if secondStart < firstStart {
|
||||
t.Error("Second Start() affected timing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopwatch_DoubleStop(t *testing.T) {
|
||||
sw := &Stopwatch{}
|
||||
|
||||
sw.Start()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
sw.Stop()
|
||||
elapsed := sw.Elapsed()
|
||||
|
||||
// Second Stop() should be no-op
|
||||
sw.Stop()
|
||||
if sw.Elapsed() != elapsed {
|
||||
t.Error("Second Stop() changed elapsed time")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopwatch_ConcurrentAccess(t *testing.T) {
|
||||
sw := &Stopwatch{}
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Test concurrent Start/Stop/Elapsed calls
|
||||
// This should not cause data races
|
||||
const goroutines = 10
|
||||
const iterations = 100
|
||||
|
||||
wg.Add(goroutines * 3)
|
||||
|
||||
// Concurrent starts
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < iterations; j++ {
|
||||
sw.Start()
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Concurrent stops
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < iterations; j++ {
|
||||
sw.Stop()
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Concurrent reads
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < iterations; j++ {
|
||||
_ = sw.Elapsed()
|
||||
time.Sleep(time.Microsecond)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// If we got here without data races, the test passes
|
||||
// Run with: go test -race
|
||||
}
|
||||
|
||||
func TestStopwatch_Reset(t *testing.T) {
|
||||
sw := &Stopwatch{}
|
||||
|
||||
// Reset when stopped
|
||||
sw.Reset()
|
||||
if elapsed := sw.Elapsed(); elapsed != 0 {
|
||||
t.Errorf("Expected 0 after reset, got %v", elapsed)
|
||||
}
|
||||
|
||||
// Reset when running
|
||||
sw.Start()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
sw.Reset()
|
||||
if elapsed := sw.Elapsed(); elapsed != 0 {
|
||||
t.Errorf("Expected 0 after reset while running, got %v", elapsed)
|
||||
}
|
||||
|
||||
// After reset, should be able to start again
|
||||
sw.Start()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
elapsed := sw.Elapsed()
|
||||
if elapsed < 5*time.Millisecond {
|
||||
t.Errorf("Expected at least 5ms after reset and start, got %v", elapsed)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user