Skip to content

Instantly share code, notes, and snippets.

@yanisurbis
Created September 20, 2022 15:17
Show Gist options
  • Save yanisurbis/d47e6559d7dcb8957c77cd30d27e94d2 to your computer and use it in GitHub Desktop.
Save yanisurbis/d47e6559d7dcb8957c77cd30d27e94d2 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"log"
"net"
"os"
"os/exec"
"strings"
"sync"
"time"
"bytes"
"math/rand"
)
const AddressWithPort = "localhost:10823"
const PathToExecutable = "./gfclient_download"
type TestCase struct {
id int
description string
filePath string
handler func(*sync.WaitGroup, net.Conn, *TestCase)
assert func(string, error, *TestCase)
}
func contains(x string, y string) {
if !strings.Contains(x, y) {
log.Fatalf("should contain %s\n", y)
}
}
func failIfNot(v bool, msg string) {
if !v {
log.Fatalf(msg)
}
}
func filePathSentByServer(path string) string {
return ".." + path
}
func filePathReceivedByClient(path string) string {
return "../received_by_client" + path
}
func compareSentAndReceivedFiles(path string) {
bytesSentFile, err := os.ReadFile(filePathSentByServer(path))
if err != nil {
log.Fatal("failed to open a file, ", err)
}
bytesReceivedFile, err := os.ReadFile(filePathReceivedByClient(path))
if err != nil {
log.Fatal("failed to open a file, ", err)
}
if bytes.Compare(bytesSentFile, bytesReceivedFile) != 0 {
log.Fatalf("Files %s should be equal", path)
}
}
func startSeverUsingHandler(
wg *sync.WaitGroup,
testCase *TestCase) {
listen, err := net.Listen("tcp", AddressWithPort)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
go runProgram(wg, testCase)
fmt.Printf("testing: %s\n", testCase.description)
conn, err := listen.Accept()
if err != nil {
log.Fatal(err)
}
go testCase.handler(wg, conn, testCase)
listen.Close()
fmt.Printf("Ok\n\n")
}
func DEBUG_HANDLER(
wg *sync.WaitGroup,
testCase *TestCase) {
listen, err := net.Listen("tcp", AddressWithPort)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
fmt.Printf("testing: %s\n", testCase.description)
for {
conn, err := listen.Accept()
if err != nil {
log.Fatal(err)
}
//go func(conn net.Conn) {
// <-time.After(time.Duration(3) * time.Second)
// fmt.Println("Closing connection...")
// defer conn.Close()
//}(conn)
go testCase.handler(wg, conn, testCase)
}
}
func runProgram(wg *sync.WaitGroup, tc *TestCase) {
var cmd *exec.Cmd
if tc.filePath != "" {
cmd = exec.Command(PathToExecutable, "-f", tc.filePath)
} else {
cmd = exec.Command(PathToExecutable)
}
cmd.Dir = ".."
stdout, err := cmd.Output()
if err != nil {
fmt.Println(err.Error())
return
}
//fmt.Print(string(stdout))
tc.assert(string(stdout), err, tc)
wg.Done()
}
func main() {
cases := []TestCase{
{
id: 0,
description: "basic case",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE OK 6\r\n\r\nabcdef"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: OK")
contains(out, "Received 6 of 6 bytes")
failIfNot(err == nil, "StdErr should be empty")
},
},
{
id: 0,
description: "send wrong delimeter",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE "))
conn.Write([]byte("OK 6\n\na"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: INVALID")
contains(out, "Received 0 of 0 bytes")
},
},
{
id: 0,
description: "don't send full body before closing",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE "))
conn.Write([]byte("OK 6\r\n\r\na"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: OK")
contains(out, "Received 1 of 6 bytes")
},
},
{
id: 0,
description: "don't send full header",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE OK"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: INVALID")
contains(out, "Received 0 of 0 bytes")
},
},
{
id: 0,
description: "send ERROR back",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE ERROR\r\n\r\n"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: ERROR")
contains(out, "Received 0 of 0 bytes")
},
},
{
id: 0,
description: "send FILE_NOT_FOUND back",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE FILE_NOT_FOUND\r\n\r\n"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: FILE_NOT_FOUND")
contains(out, "Received 0 of 0 bytes")
},
},
{
id: 0,
description: "broken header",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
// respond
conn.Write([]byte("GETFILE xasdfasdf 123\r\n\r\n"))
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: INVALID")
contains(out, "Received 0 of 0 bytes")
},
},
{
id: 0,
description: "small file in one chunk",
filePath: "/test_files/digits.txt",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
fileAsByteArray, err := os.ReadFile(filePathSentByServer(tc.filePath))
if err != nil {
log.Fatal("failed to open a file, ", err)
}
// respond
conn.Write([]byte(fmt.Sprintf("GETFILE OK %d\r\n\r\n", len(fileAsByteArray))))
conn.Write(fileAsByteArray)
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: OK")
compareSentAndReceivedFiles(tc.filePath)
},
},
{
id: 0,
description: "don't fully send header, don't close a socket",
filePath: "",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
conn.Write([]byte("GETFILE OK"))
<-time.After(time.Duration(1) * time.Second)
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: INVALID")
contains(out, "Received 0 of 0 bytes")
},
},
{
id: 0,
description: "don't fully send body, don't close a socket",
filePath: "/test_files/digits.txt",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
fileAsByteArray, err := os.ReadFile(filePathSentByServer(tc.filePath))
if err != nil {
log.Fatal("failed to open a file, ", err)
}
// respond
conn.Write([]byte(fmt.Sprintf("GETFILE OK %d\r\n\r\n", len(fileAsByteArray))))
conn.Write(fileAsByteArray[:3])
<-time.After(time.Duration(1) * time.Second)
// close conn
//conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: OK")
contains(out, "Received 3 of 10 bytes")
},
},
{
id: 0,
description: "small file in one chunk, don't close a socket",
filePath: "/test_files/digits.txt",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
fileAsByteArray, err := os.ReadFile(filePathSentByServer(tc.filePath))
if err != nil {
log.Fatal("failed to open a file, ", err)
}
// respond
conn.Write([]byte(fmt.Sprintf("GETFILE OK %d\r\n\r\n", len(fileAsByteArray))))
conn.Write(fileAsByteArray)
<-time.After(time.Duration(1) * time.Second)
// close conn
//conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: OK")
compareSentAndReceivedFiles(tc.filePath)
},
},
{
id: 0,
description: "big file in mulitple chunks",
filePath: "/test_files/the_idiot.txt",
handler: func(wg *sync.WaitGroup, conn net.Conn, tc *TestCase) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
fileAsByteArray, err := os.ReadFile(filePathSentByServer(tc.filePath))
if err != nil {
log.Fatal("failed to open a file, ", err)
}
// respond
conn.Write([]byte(fmt.Sprintf("GETFILE OK %d\r\n\r\n", len(fileAsByteArray))))
length := len(fileAsByteArray)
for i := 0; i < length; i++ {
n := rand.Intn(64000)
if length - i < n {
n = length - i
}
conn.Write(fileAsByteArray[i: i + n + 1])
i += n
}
// close conn
conn.Close()
wg.Done()
},
assert: func(out string, err error, tc *TestCase) {
contains(out, "Status: OK")
compareSentAndReceivedFiles(tc.filePath)
},
},
}
var wg sync.WaitGroup
fmt.Println("start testing!\n")
for _, c := range cases {
wg.Add(2)
go startSeverUsingHandler(&wg, &c)
// Isolate a specific test before uncommenting the line below
//go DEBUG_HANDLER(&wg, &c)
wg.Wait()
}
fmt.Println("success!")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment