Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions tftp_anticipate_test.go → anticipate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import (

// derived from Test900
func TestAnticipateWindow900(t *testing.T) {
s, c := makeTestServerAnticipateWindow()
s, c := makeTestServerAnticipateWindow(t)
defer s.Shutdown()
for i := 600; i < 4000; i++ {
c.blksize = i
c.SetBlockSize(i)
testSendReceive(t, c, 9000+int64(i))
}
}

// TestAnticipateHookSuccess verifies that OnSuccess hook is called on transfer completion when SetAnticipate is used
func TestAnticipateHookSuccess(t *testing.T) {
s, c := makeTestServerAnticipateWindow()
s, c := makeTestServerAnticipateWindow(t)
th := newTestHook()
s.SetHook(th)
testSendReceive(t, c, 154242)
Expand All @@ -30,7 +30,8 @@ func TestAnticipateHookSuccess(t *testing.T) {
}

// derived from makeTestServer
func makeTestServerAnticipateWindow() (*Server, *Client) {
func makeTestServerAnticipateWindow(t *testing.T) (*Server, *Client) {
t.Helper()
b := &testBackend{}
b.m = make(map[string][]byte)

Expand All @@ -40,15 +41,15 @@ func makeTestServerAnticipateWindow() (*Server, *Client) {

conn, err := net.ListenUDP("udp", &net.UDPAddr{})
if err != nil {
panic(err)
t.Fatalf("listen udp: %v", err)
}

go s.Serve(conn)

// Create client for that server
c, err := NewClient(localSystem(conn))
c, err := NewClient(localSystem(t, conn))
if err != nil {
panic(err)
t.Fatalf("new client: %v", err)
}

return s, c
Expand Down
169 changes: 169 additions & 0 deletions api_error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package tftp

import (
"bytes"
"errors"
"fmt"
"io"
"math/rand"
"net"
"testing"
)

func TestDuplicate(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
defer s.Shutdown()
filename := "test-duplicate"
xferMode := "octet"
bs := []byte("lalala")
sender, err := c.Send(filename, xferMode)
if err != nil {
t.Fatalf("requesting write: %v", err)
}
buf := bytes.NewBuffer(bs)
_, err = sender.ReadFrom(buf)
if err != nil {
t.Fatalf("send error: %v", err)
}
_, err = c.Send(filename, xferMode)
if err == nil {
t.Fatalf("file already exists")
}
t.Logf("sending file that already exists: %v", err)
})
}

func TestNotFound(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
defer s.Shutdown()
filename := "test-not-exists"
xferMode := "octet"
_, err := c.Receive(filename, xferMode)
if err == nil {
t.Fatalf("file not exists: %v", err)
}
t.Logf("receiving file that does not exist: %v", err)
})
}

func TestNoHandlers(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s := NewServer(nil, nil)
if mode == modeSinglePort {
s.EnableSinglePort()
}

conn, err := net.ListenUDP("udp", &net.UDPAddr{})
if err != nil {
t.Fatalf("listen udp: %v", err)
}

go s.Serve(conn)
defer s.Shutdown()

c, err := NewClient(localSystem(t, conn))
if err != nil {
t.Fatalf("new client: %v", err)
}

_, err = c.Send("test", "octet")
if err == nil {
t.Errorf("error expected")
}

_, err = c.Receive("test", "octet")
if err == nil {
t.Errorf("error expected")
}
})
}

// TestFileIOExceptions checks that errors returned by io.Reader or io.Writer used by
// the handler are handled correctly.
func TestReadWriteErrors(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s := NewServer(
func(_ string, rf io.ReaderFrom) error {
_, err := rf.ReadFrom(&failingReader{}) // Read operation fails immediately.
if err != errRead {
t.Errorf("want: %v, got: %v", errRead, err)
}
// return no error from handler, client still should receive error
return nil
},
func(_ string, wt io.WriterTo) error {
_, err := wt.WriteTo(&failingWriter{}) // Write operation fails immediately.
if err != errWrite {
t.Errorf("want: %v, got: %v", errWrite, err)
}
// return no error from handler, client still should receive error
return nil
},
)
if mode == modeSinglePort {
s.EnableSinglePort()
}

conn, err := net.ListenUDP("udp", &net.UDPAddr{})
if err != nil {
t.Fatalf("listen UDP: %v", err)
}

// Start server
errChan := make(chan error, 1)
go func() {
err := s.Serve(conn)
if err != nil {
errChan <- fmt.Errorf("running serve: %w", err)
}
}()
defer func() {
s.Shutdown()
select {
case err := <-errChan:
t.Errorf("server error: %v", err)
default:
}
}()

// Create client
c, err := NewClient(localSystem(t, conn))
if err != nil {
t.Fatalf("creating new client: %v", err)
}

ot, err := c.Send("a", "octet")
if err != nil {
t.Errorf("start sending: %v", err)
}

_, err = ot.ReadFrom(io.LimitReader(
newRandReader(rand.NewSource(42)), 42))
if err == nil {
t.Errorf("missing write error")
}

_, err = c.Receive("a", "octet")
if err == nil {
t.Errorf("missing read error")
}
})
}

type failingReader struct{}

var errRead = errors.New("read error")

func (r *failingReader) Read(_ []byte) (int, error) {
return 0, errRead
}

type failingWriter struct{}

var errWrite = errors.New("write error")

func (r *failingWriter) Write(_ []byte) (int, error) {
return 0, errWrite
}
38 changes: 37 additions & 1 deletion blocksize_negotiation_test.go → blocksize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,42 @@ import (
"time"
)

func Test900(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
defer s.Shutdown()
for i := 600; i < 4000; i++ {
c.SetBlockSize(i)
// In single-port mode the server read loop continuously reads maxBlockLen,
// so mutating block size at runtime races with Serve under -race.
// Keep runtime server mutation only in regular mode.
if mode == modeRegular {
s.SetBlockSize(4600 - i)
}
testSendReceive(t, c, 9000+int64(i))
}
})
}

func Test1810(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
defer s.Shutdown()
c.SetBlockSize(1810)
testSendReceive(t, c, 9000+1810)
})
}

func TestNearBlockLength(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
defer s.Shutdown()
for i := 450; i < 520; i++ {
testSendReceive(t, c, int64(i))
}
})
}

func TestBlockSizeNegotiation_ClampsByPathLimit(t *testing.T) {
got := negotiateBlockSizeForTest(t, 65432, 1472, true)
if got != "1472" {
Expand Down Expand Up @@ -154,7 +190,7 @@ func BenchmarkBlockSizeNegotiation(b *testing.B) {
go func() { _ = s.Serve(conn) }()
b.Cleanup(s.Shutdown)

c, err := NewClient(localSystem(conn))
c, err := NewClient(localSystem(b, conn))
if err != nil {
b.Fatalf("new client: %v", err)
}
Expand Down
59 changes: 59 additions & 0 deletions hooks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tftp

import (
"fmt"
"io"
"math/rand"
"testing"
"time"
)

func TestHookSuccess(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
th := newTestHook()
s.SetHook(th)
c.SetBlockSize(1810)
length := int64(9000)
filename := fmt.Sprintf("length-%d-bytes-%d", length, time.Now().UnixNano())
rf, err := c.Send(filename, "octet")
if err != nil {
t.Fatalf("requesting %s write: %v", filename, err)
}
r := io.LimitReader(newRandReader(rand.NewSource(length)), length)
n, err := rf.ReadFrom(r)
if err != nil {
t.Fatalf("sending %s: %v", filename, err)
}
if n != length {
t.Errorf("%s length mismatch: %d != %d", filename, n, length)
}
s.Shutdown()
th.Lock()
defer th.Unlock()
if th.transfersCompleted != 1 {
t.Errorf("unexpected completed transfers count: %d", th.transfersCompleted)
}
})
}

func TestHookFailure(t *testing.T) {
forModes(t, func(t *testing.T, mode transferMode) {
s, c := newFixture(t, mode)
th := newTestHook()
s.SetHook(th)
filename := "test-not-exists"
xferMode := "octet"
_, err := c.Receive(filename, xferMode)
if err == nil {
t.Fatalf("file not exists: %v", err)
}
t.Logf("receiving file that does not exist: %v", err)
s.Shutdown()
th.Lock()
defer th.Unlock()
if th.transfersFailed == 0 { // TODO: there are two failures, not one on Windows?
t.Errorf("unexpected failed transfers count: %d", th.transfersFailed)
}
})
}
Loading