package hopp import "io" import "net" import "bytes" import "errors" import "slices" import "testing" // some of these tests spawn goroutines that can signal a failure. // abide by the documentation for testing.T (https://pkg.go.dev/testing#T): // // A test ends when its Test function returns or calls any of the methods // FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as // the Parallel method, must be called only from the goroutine running the // Test function. // // The other reporting methods, such as the variations of Log and Error, may // be called simultaneously from multiple goroutines. func TestConnA(test *testing.T) { payloads := []string { "hello", "world", "When the impostor is sus!", } clientFunc := func(a Conn) { test.Log("CLIENT accepting transaction") trans, err := a.AcceptTrans() if err != nil { test.Fatal("CLIENT", err) } test.Log("CLIENT accepted transaction") test.Cleanup(func() { trans.Close() }) for method, payload := range payloads { test.Log("CLIENT waiting...") gotMethod, gotPayloadBytes, err := trans.Receive() if err != nil { test.Fatal("CLIENT", err) } gotPayload := string(gotPayloadBytes) test.Log("CLIENT m:", gotMethod, "p:", gotPayload) if int(gotMethod) != method { test.Errorf("CLIENT method not equal") } if gotPayload != payload { test.Errorf("CLIENT payload not equal") } } test.Log("CLIENT waiting for transaction close...") gotMethod, gotPayload, err := trans.Receive() if !errors.Is(err, io.EOF) { test.Error("CLIENT wrong error:", err) test.Error("CLIENT method:", gotMethod) test.Error("CLIENT payload:", gotPayload) test.Fatal("CLIENT ok byeeeeeeeeeeeee") } } serverFunc := func(a Conn) { trans, err := a.OpenTrans() if err != nil { test.Error("SERVER", err); return } test.Cleanup(func() { trans.Close() }) for method, payload := range payloads { test.Log("SERVER m:", method, "p:", payload) err := trans.Send(uint16(method), []byte(payload)) if err != nil { test.Error("SERVER", err); return } } test.Log("SERVER closing connection") } clientServerEnvironment(test, clientFunc, serverFunc) } func TestTransOpenCloseA(test *testing.T) { // currently: // // | data sent | data recvd | close sent | close recvd // 10 | X | X | X | server hangs // 20 | X | X | X | client hangs // 30 | X | | X | // // when a close message is recvd, it tries to push to the trans and // hangs on trans.incoming.Send, which hangs on sending the value to the // underlying channel. why is this? // // check if we are really getting values from the channel when pulling // from the trans channel when we are expecting a close. clientFunc := func(conn Conn) { // 10 trans, err := conn.OpenTrans() if err != nil { test.Error("CLIENT", err); return } test.Log("CLIENT sending 10") trans.Send(10, []byte("hi")) trans.Close() // 20 test.Log("CLIENT awaiting 20") trans, err = conn.AcceptTrans() if err != nil { test.Error("CLIENT", err); return } test.Cleanup(func() { trans.Close() }) gotMethod, gotPayload, err := trans.Receive() if err != nil { test.Error("CLIENT", err); return } test.Logf("CLIENT m: %d p: %s", gotMethod, gotPayload) if gotMethod != 20 { test.Error("CLIENT wrong method")} // 30 trans, err = conn.OpenTrans() if err != nil { test.Error("CLIENT", err); return } test.Log("CLIENT sending 30") trans.Send(30, []byte("good")) trans.Close() } serverFunc := func(conn Conn) { // 10 test.Log("SERVER awaiting 10") trans, err := conn.AcceptTrans() if err != nil { test.Error("SERVER", err); return } test.Cleanup(func() { trans.Close() }) gotMethod, gotPayload, err := trans.Receive() if err != nil { test.Error("SERVER", err); return } test.Logf("SERVER m: %d p: %s", gotMethod, gotPayload) if gotMethod != 10 { test.Error("SERVER wrong method")} // 20 trans, err = conn.OpenTrans() if err != nil { test.Error("SERVER", err); return } test.Log("SERVER sending 20") trans.Send(20, []byte("hi how r u")) trans.Close() // 30 test.Log("SERVER awaiting 30") trans, err = conn.AcceptTrans() if err != nil { test.Error("SERVER", err); return } test.Cleanup(func() { trans.Close() }) gotMethod, gotPayload, err = trans.Receive() if err != nil { test.Error("SERVER", err); return } test.Logf("SERVER m: %d p: %s", gotMethod, gotPayload) if gotMethod != 30 { test.Error("SERVER wrong method")} } clientServerEnvironment(test, clientFunc, serverFunc) } func TestEncodeMessageA(test *testing.T) { buffer := new(bytes.Buffer) payload := []byte { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 } err := encodeMessageA(buffer, defaultSizeLimit, 0x5800FEABC3104F04, 0x6B12, payload) correct := []byte { 0x58, 0x00, 0xFE, 0xAB, 0xC3, 0x10, 0x4F, 0x04, 0x6B, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, } if err != nil { test.Fatal(err) } if got, correct := buffer.Bytes(), correct; !slices.Equal(got, correct) { test.Fatalf("not equal: %v %v", got, correct) } } func TestEncodeMessageAErr(test *testing.T) { buffer := new(bytes.Buffer) payload := make([]byte, 0x10000) err := encodeMessageA(buffer, 0x20, 0x5800FEABC3104F04, 0x6B12, payload) if !errors.Is(err, ErrPayloadTooLarge) { test.Fatalf("wrong error: %v", err) } } func TestDecodeMessageA(test *testing.T) { transID, method, _, payload, err := decodeMessageA(bytes.NewReader([]byte { 0x58, 0x00, 0xFE, 0xAB, 0xC3, 0x10, 0x4F, 0x04, 0x6B, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, }), defaultSizeLimit) if err != nil { test.Fatal(err) } if got, correct := transID, int64(0x5800FEABC3104F04); got != correct { test.Fatalf("not equal: %v %v", got, correct) } if got, correct := method, uint16(0x6B12); got != correct { test.Fatalf("not equal: %v %v", got, correct) } correctPayload := []byte { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 } if got, correct := payload, correctPayload; !slices.Equal(got, correct) { test.Fatalf("not equal: %v %v", got, correct) } } func TestDecodeMessageAErr(test *testing.T) { _, _, _, _, err := decodeMessageA(bytes.NewReader([]byte { 0x58, 0x00, 0xFE, 0xAB, 0xC3, 0x10, 0x4F, 0x04, 0x6B, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, }), defaultSizeLimit) if !errors.Is(err, io.ErrUnexpectedEOF) { test.Fatalf("wrong error: %v", err) } } func TestEncodeDecodeMessageA(test *testing.T) { correctTransID := int64(2) correctMethod := uint16(30) correctPayload := []byte("good") buffer := bytes.Buffer { } err := encodeMessageA(&buffer, defaultSizeLimit, correctTransID, correctMethod, correctPayload) if err != nil { test.Fatal(err) } transID, method, chunked, payload, err := decodeMessageA(&buffer, defaultSizeLimit) if got, correct := transID, int64(2); got != correct { test.Fatalf("not equal: %v %v", got, correct) } if got, correct := method, uint16(30); got != correct { test.Fatalf("not equal: %v %v", got, correct) } if chunked { test.Fatalf("message should not be chunked") } if got, correct := payload, correctPayload; !slices.Equal(got, correct) { test.Fatalf("not equal: %v %v", got, correct) } } func clientServerEnvironment(test *testing.T, clientFunc func(conn Conn), serverFunc func(conn Conn)) { network := "tcp" addr := "localhost:7959" // server listener, err := net.Listen(network, addr) if err != nil { test.Fatal(err) } test.Cleanup(func() { listener.Close() }) go func() { test.Log("SERVER listening") conn, err := listener.Accept() if err != nil { test.Error("SERVER", err); return } defer conn.Close() test.Cleanup(func() { conn.Close() }) a := AdaptA(conn, ServerSide) test.Cleanup(func() { a.Close() }) serverFunc(a) test.Log("SERVER closing") }() // client test.Log("CLIENT dialing") conn, err := net.Dial(network, addr) if err != nil { test.Fatal("CLIENT", err) } test.Log("CLIENT dialed") a := AdaptA(conn, ClientSide) test.Cleanup(func() { a.Close() }) clientFunc(a) test.Log("CLIENT waiting for connection close...") trans, err := a.AcceptTrans() if !errors.Is(err, io.EOF) { test.Error("CLIENT wrong error:", err) test.Fatal("CLIENT trans:", trans) } test.Log("CLIENT DONE") conn.Close() }