flutter-tsnet/test/linux_e2e.go
Mathias Beaulieu-Duncan 623407d16b Update README with full platform docs, API reference, and binary sizes
- Document all 4 platforms with status and architecture details
- Add API reference table
- Add platform setup sections (iOS, macOS, Android, Linux)
- Document build commands for all platforms
- Add binary size table
- Note Android Tailscale tunnel limitation and path forward
- Add Linux test file

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 07:58:38 -04:00

117 lines
2.9 KiB
Go

// Linux test for tsnet — loads the shared library via CGo and tests the tunnel.
//
// Run from macOS via Docker:
// docker run --rm --platform linux/amd64 \
// -v /path/to/flutter-tsnet:/src -w /src/test \
// golang:latest go run linux_test.go \
// -authkey=tskey-auth-xxx \
// -heater-ip=100.x.x.x
package main
/*
#cgo LDFLAGS: -L${SRCDIR}/../linux -ltailscale -lresolv -lpthread
#include <stdlib.h>
extern char* TailscaleStart(char*, char*, char*);
extern char* TailscaleStartProxy(char*, int, int*);
extern char* TailscaleStopProxy();
extern char* TailscaleStop();
extern char* TailscaleStatus(char**);
extern char* TailscaleIP(char**);
extern void TailscaleFreeString(char*);
*/
import "C"
import (
"flag"
"fmt"
"net"
"os"
"time"
"unsafe"
)
func goString(cs *C.char) string {
if cs == nil {
return ""
}
s := C.GoString(cs)
C.TailscaleFreeString(cs)
return s
}
func main() {
authKey := flag.String("authkey", "", "Tailscale auth key")
heaterIP := flag.String("heater-ip", "", "Heater's Tailscale IP")
heaterPort := flag.Int("heater-port", 5050, "Heater's port")
flag.Parse()
if *authKey == "" || *heaterIP == "" {
fmt.Fprintf(os.Stderr, "Usage: go run linux_test.go -authkey=tskey-auth-xxx -heater-ip=100.x.x.x\n")
os.Exit(1)
}
// State dir
stateDir := "/tmp/tsnet-linux-test"
os.MkdirAll(stateDir, 0o700)
// 1. Start
fmt.Println("[1/4] Starting tsnet...")
start := time.Now()
dirC := C.CString(stateDir)
keyC := C.CString(*authKey)
hostC := C.CString("tsnet-linux-test")
err := goString(C.TailscaleStart(dirC, keyC, hostC))
C.free(unsafe.Pointer(dirC))
C.free(unsafe.Pointer(keyC))
C.free(unsafe.Pointer(hostC))
if err != "" {
fmt.Printf("[1/4] FAIL: %s\n", err)
os.Exit(1)
}
fmt.Printf("[1/4] Started in %dms\n", time.Since(start).Milliseconds())
// 2. Get IP (with retry)
fmt.Println("[2/4] Getting Tailscale IP...")
var ip string
for i := 0; i < 30; i++ {
var errOut *C.char
ipC := C.TailscaleIP(&errOut)
e := goString(errOut)
if e == "" {
ip = goString(ipC)
break
}
C.TailscaleFreeString(ipC)
time.Sleep(500 * time.Millisecond)
}
fmt.Printf("[2/4] Our IP: %s\n", ip)
// 3. Start proxy
fmt.Println("[3/4] Starting proxy...")
ipC := C.CString(*heaterIP)
var localPort C.int
errStr := goString(C.TailscaleStartProxy(ipC, C.int(*heaterPort), &localPort))
C.free(unsafe.Pointer(ipC))
if errStr != "" {
fmt.Printf("[3/4] FAIL: %s\n", errStr)
os.Exit(1)
}
fmt.Printf("[3/4] Proxy: localhost:%d → %s:%d\n", int(localPort), *heaterIP, *heaterPort)
// 4. Test TCP
fmt.Println("[4/4] Testing TCP connection...")
conn, connErr := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", int(localPort)), 10*time.Second)
if connErr != nil {
fmt.Printf("[4/4] TCP FAILED: %v\n", connErr)
} else {
fmt.Println("[4/4] TCP SUCCESS")
conn.Close()
}
// Cleanup
C.TailscaleStopProxy()
C.TailscaleStop()
fmt.Println("\nALL TESTS PASSED")
}