Skip to content

Commit

Permalink
moving test to integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
mfreeman451 committed Feb 22, 2025
1 parent c44a56b commit 6985283
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 61 deletions.
96 changes: 96 additions & 0 deletions pkg/scan/icmp_scanner_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,102 @@ func skipIfNotIntegration(t *testing.T) {
}
}

func TestSocketPool(t *testing.T) {
skipIfNotIntegration(t)

t.Run("Concurrent Access", func(t *testing.T) {
pool := newSocketPool(5, defaultMaxSocketAge, defaultMaxIdleTime)
defer pool.close()

var wg sync.WaitGroup
numGoroutines := 15
successCount := atomic.Int32{}
poolFullCount := atomic.Int32{}
otherErrorCount := atomic.Int32{}

for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()

conn, release, err := pool.getSocket()
if err != nil {
if errors.Is(err, errNoAvailableSocketsInPool) {
poolFullCount.Add(1)
} else {
otherErrorCount.Add(1)
t.Errorf("Unexpected error: %v", err)
}
return
}

if conn == nil {
t.Error("Got nil connection with no error")
otherErrorCount.Add(1)
return
}

successCount.Add(1)
// Simulate brief usage without sleep
_ = conn.SetReadDeadline(time.Now().Add(1 * time.Millisecond))
release()
}()
}

wg.Wait()

assert.Equal(t, int32(0), otherErrorCount.Load(), "Should not have any unexpected errors")
assert.Equal(t, int32(5), successCount.Load(), "Should have exactly maxSockets successful acquisitions")
assert.Equal(t, int32(numGoroutines-5), poolFullCount.Load(), "Should have pool-full conditions for excess goroutines")
assert.Equal(t, numGoroutines, int(successCount.Load()+poolFullCount.Load()), "Total attempts should equal number of goroutines")
})

t.Run("Socket Recycling", func(t *testing.T) {
pool := newSocketPool(1, 1*time.Millisecond, defaultMaxIdleTime)
defer pool.close()

// Get an initial socket to populate the pool
conn, release, err := pool.getSocket()
require.NoError(t, err)
require.NotNil(t, conn)
initialCreatedAt := pool.sockets[0].createdAt
release()

// Force cleanup to mark the socket as stale
pool.cleanup()

// Get a new socket, which should recycle or create a new one
conn, release, err = pool.getSocket()
require.NoError(t, err, "Should not error when requesting a new socket after cleanup")
require.NotNil(t, conn, "Connection should not be nil")
assert.Equal(t, 1, len(pool.sockets), "Should still have one socket")
assert.True(t, pool.sockets[0].createdAt.After(initialCreatedAt), "New socket should have a newer creation time")
assert.False(t, pool.sockets[0].closed.Load(), "Socket should not be closed")
release()
})

t.Run("Pool Closure", func(t *testing.T) {
pool := newSocketPool(2, defaultMaxSocketAge, defaultMaxIdleTime)
defer pool.close()

// Populate with two sockets
for i := 0; i < 2; i++ {
conn, release, err := pool.getSocket()
require.NoError(t, err)
require.NotNil(t, conn)
release()
}

pool.close()

for _, entry := range pool.sockets {
assert.True(t, entry.closed.Load(), "All sockets should be closed")
}
assert.Nil(t, pool.cleaner, "Cleaner should be stopped")
assert.Nil(t, pool.sockets, "Sockets slice should be nil after close")
})
}

func TestICMPScannerIntegration(t *testing.T) {
skipIfNotIntegration(t)

Expand Down
105 changes: 44 additions & 61 deletions pkg/scan/icmp_scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package scan
import (
"context"
"errors"
"net"
"sync"
"sync/atomic"
"testing"
"time"

Expand Down Expand Up @@ -48,66 +48,6 @@ func TestICMPChecksum(t *testing.T) {
}
}

func TestSocketPool(t *testing.T) {
t.Run("Concurrent Access", func(t *testing.T) {
pool := newSocketPool(5, time.Minute, time.Minute)
defer pool.close()

var wg sync.WaitGroup

numGoroutines := 10

successCount := atomic.Int32{}
poolFullCount := atomic.Int32{}
otherErrorCount := atomic.Int32{}

for i := 0; i < numGoroutines; i++ {
wg.Add(1)

go func() {
defer wg.Done()

conn, release, err := pool.getSocket()
if err != nil {
if errors.Is(err, errNoAvailableSocketsInPool) {
poolFullCount.Add(1)
} else {
otherErrorCount.Add(1)
t.Errorf("Unexpected error: %v", err)
}

return
}

if conn == nil {
t.Error("Got nil connection with no error")
otherErrorCount.Add(1)

return
}

// Just verify the connection exists
successCount.Add(1)

// Simulate some work without touching the connection
time.Sleep(10 * time.Millisecond)

// Release the socket back to the pool
release()
}()
}

wg.Wait()

// Verify results
assert.Equal(t, int32(0), otherErrorCount.Load(), "Should not have any unexpected errors") // Modified line 104
assert.Positive(t, successCount.Load(), "Should have some successful socket acquisitions")
assert.Positive(t, poolFullCount.Load(), "Should have some pool-full conditions")
assert.Equal(t, numGoroutines, int(successCount.Load()+poolFullCount.Load()),
"Total attempts should equal number of goroutines")
})
}

func TestICMPScanner(t *testing.T) {
t.Run("Scanner Creation", func(t *testing.T) {
scanner, err := NewICMPScanner(time.Second, 5, 3)
Expand Down Expand Up @@ -260,3 +200,46 @@ func TestAtomicOperations(t *testing.T) {
assert.Equal(t, int32(0), entry.inUse.Load())
})
}

// mockConn is a minimal interface for the socket pool's connection needs in tests.
type mockConn interface {

Check failure on line 205 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

type `mockConn` is unused (unused)
Close() error
WriteTo([]byte, net.Addr) (int, error)
ReadFrom([]byte) (int, net.Addr, error)
SetReadDeadline(t time.Time) error
}

// mockPacketConn implements mockConn for testing.
type mockPacketConn struct {

Check failure on line 213 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

type `mockPacketConn` is unused (unused)
closed bool
mu sync.Mutex
}

func (m *mockPacketConn) Close() error {

Check failure on line 218 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

func `(*mockPacketConn).Close` is unused (unused)
m.mu.Lock()
defer m.mu.Unlock()
m.closed = true
return nil

Check failure on line 222 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

return statements should not be cuddled if block has more than two lines (wsl)
}

func (m *mockPacketConn) WriteTo([]byte, net.Addr) (int, error) {

Check failure on line 225 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

func `(*mockPacketConn).WriteTo` is unused (unused)
m.mu.Lock()
defer m.mu.Unlock()
if m.closed {

Check failure on line 228 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

if statements should only be cuddled with assignments (wsl)
return 0, errors.New("connection closed")

Check failure on line 229 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

do not define dynamic errors, use wrapped static errors instead: "errors.New(\"connection closed\")" (err113)
}
return 0, nil
}

func (m *mockPacketConn) ReadFrom([]byte) (int, net.Addr, error) {

Check failure on line 234 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

func `(*mockPacketConn).ReadFrom` is unused (unused)
m.mu.Lock()
defer m.mu.Unlock()
if m.closed {
return 0, nil, errors.New("connection closed")

Check failure on line 238 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

do not define dynamic errors, use wrapped static errors instead: "errors.New(\"connection closed\")" (err113)
}
return 0, nil, nil
}

func (m *mockPacketConn) SetReadDeadline(t time.Time) error {

Check failure on line 243 in pkg/scan/icmp_scanner_test.go

View workflow job for this annotation

GitHub Actions / lint

unused-parameter: parameter 't' seems to be unused, consider removing or renaming it as _ (revive)
return nil
}

0 comments on commit 6985283

Please sign in to comment.