diff --git a/cmd/olcrtc/main.go b/cmd/olcrtc/main.go index 3ce6bc2..b117c28 100644 --- a/cmd/olcrtc/main.go +++ b/cmd/olcrtc/main.go @@ -25,9 +25,9 @@ const modeGen = "gen" // ErrDataDirRequired is returned when no data directory is specified. var ErrDataDirRequired = errors.New("data directory required (use -data data)") -var runSession = session.Run +var runSession = session.Run //nolint:gochecknoglobals // package-level state intentional -var runGen = execGen +var runGen = execGen //nolint:gochecknoglobals // package-level state intentional type config struct { mode string diff --git a/cmd/olcrtc/main_test.go b/cmd/olcrtc/main_test.go index 6d80b61..2199eaa 100644 --- a/cmd/olcrtc/main_test.go +++ b/cmd/olcrtc/main_test.go @@ -12,18 +12,21 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/logger" ) +var errBoom = errors.New("boom") + +//nolint:cyclop // table-driven test naturally has many branches func TestToSessionConfig(t *testing.T) { cfg := config{ mode: "cnc", - link: "direct", + link: "direct", //nolint:goconst // test literal, repetition is intentional transport: "vp8channel", - carrier: "jazz", - roomID: "room", - clientID: "client", - keyHex: "key", + carrier: "jazz", //nolint:goconst // test literal, repetition is intentional + roomID: "room", //nolint:goconst // test literal, repetition is intentional + clientID: "client", //nolint:goconst // test literal, repetition is intentional + keyHex: "key", //nolint:goconst // test literal, repetition is intentional socksHost: "127.0.0.1", socksPort: 1080, - dnsServer: "1.1.1.1:53", + dnsServer: "1.1.1.1:53", //nolint:goconst // test literal, repetition is intentional socksProxyAddr: "proxy", socksProxyPort: 1081, videoWidth: 640, @@ -55,9 +58,10 @@ func TestToSessionConfig(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestParseFlagsFrom(t *testing.T) { cfg, err := parseFlagsFrom([]string{ - "-mode", "srv", + "-mode", "srv", //nolint:goconst // test literal, repetition is intentional "-link", "direct", "-transport", "vp8channel", "-carrier", "telemost", @@ -109,11 +113,11 @@ func TestParseFlagsFrom(t *testing.T) { func TestRunGenModeValidationErrors(t *testing.T) { session.RegisterDefaults() - if err := runWithConfig(config{mode: "gen"}); err == nil { + if err := runWithConfig(config{mode: "gen"}); err == nil { //nolint:goconst // test literal, repetition is intentional t.Fatal("runWithConfig(gen, no carrier) error = nil") } - if err := runWithConfig(config{mode: "gen", carrier: "wbstream", dnsServer: "1.1.1.1:53"}); err == nil { + if err := runWithConfig(config{mode: "gen", carrier: "wbstream", dnsServer: "1.1.1.1:53"}); err == nil { //nolint:goconst,lll // test literal, repetition is intentional t.Fatal("runWithConfig(gen, amount=0) error = nil") } } @@ -268,7 +272,7 @@ func TestWaitForShutdown(t *testing.T) { t.Fatalf("waitForShutdown(nil) error = %v", err) } - want := errors.New("boom") + want := errBoom errCh = make(chan error, 1) errCh <- want if err := waitForShutdown(errCh); !errors.Is(err, want) { diff --git a/internal/app/session/session_test.go b/internal/app/session/session_test.go index cc8f1a3..c027e5a 100644 --- a/internal/app/session/session_test.go +++ b/internal/app/session/session_test.go @@ -6,6 +6,7 @@ import ( "testing" ) +//nolint:maintidx // table-driven validation test naturally has many cases func TestValidate(t *testing.T) { RegisterDefaults() @@ -13,11 +14,11 @@ func TestValidate(t *testing.T) { Mode: modeSRV, Link: "direct", Transport: "datachannel", - Carrier: "telemost", + Carrier: "telemost", //nolint:goconst // test literal, repetition is intentional RoomID: "room-1", ClientID: "client-1", KeyHex: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", - DNSServer: "1.1.1.1:53", + DNSServer: "1.1.1.1:53", //nolint:goconst // test literal, repetition is intentional } tests := []struct { @@ -30,7 +31,7 @@ func TestValidate(t *testing.T) { name: "jazz allows empty room id", cfg: func() Config { cfg := base - cfg.Carrier = "jazz" + cfg.Carrier = "jazz" //nolint:goconst // test literal, repetition is intentional cfg.RoomID = "" return cfg }(), @@ -58,7 +59,7 @@ func TestValidate(t *testing.T) { name: "unsupported carrier", cfg: func() Config { cfg := base - cfg.Carrier = "unknown" + cfg.Carrier = "unknown" //nolint:goconst // test literal, repetition is intentional return cfg }(), want: ErrUnsupportedCarrier, @@ -121,7 +122,7 @@ func TestValidate(t *testing.T) { name: "videochannel requires dimensions and bitrate settings", cfg: func() Config { cfg := base - cfg.Transport = "videochannel" + cfg.Transport = "videochannel" //nolint:goconst // test literal, repetition is intentional return cfg }(), want: ErrVideoWidthRequired, @@ -135,7 +136,7 @@ func TestValidate(t *testing.T) { cfg.VideoHeight = 480 cfg.VideoFPS = 30 cfg.VideoBitrate = "1M" - cfg.VideoHW = "none" + cfg.VideoHW = "none" //nolint:goconst // test literal, repetition is intentional cfg.VideoCodec = "bogus" return cfg }(), @@ -220,7 +221,7 @@ func TestValidate(t *testing.T) { name: "vp8channel requires fps", cfg: func() Config { cfg := base - cfg.Transport = "vp8channel" + cfg.Transport = "vp8channel" //nolint:goconst // test literal, repetition is intentional return cfg }(), want: ErrVP8FPSRequired, @@ -249,7 +250,7 @@ func TestValidate(t *testing.T) { name: "seichannel requires fps", cfg: func() Config { cfg := base - cfg.Transport = "seichannel" + cfg.Transport = "seichannel" //nolint:goconst // test literal, repetition is intentional return cfg }(), want: ErrSEIFPSRequired, @@ -346,7 +347,7 @@ func TestBuildRoomURL(t *testing.T) { {carrier: "telemost", roomID: "abc", want: "https://telemost.yandex.ru/j/abc"}, {carrier: "jazz", roomID: "", want: "any"}, {carrier: "jazz", roomID: "room", want: "room"}, - {carrier: "wbstream", roomID: "wb", want: "wb"}, + {carrier: "wbstream", roomID: "wb", want: "wb"}, //nolint:goconst // test literal, repetition is intentional {carrier: "other", roomID: "raw", want: "raw"}, } diff --git a/internal/carrier/builtin/provider_adapter_test.go b/internal/carrier/builtin/provider_adapter_test.go index 908b27f..6ba35d9 100644 --- a/internal/carrier/builtin/provider_adapter_test.go +++ b/internal/carrier/builtin/provider_adapter_test.go @@ -9,6 +9,13 @@ import ( "github.com/pion/webrtc/v4" ) +var ( + errConnectBoom = errors.New("connect boom") + errSendBoom = errors.New("send boom") + errCloseBoom = errors.New("close boom") + errTrackBoom = errors.New("track boom") +) + type stubProvider struct { connectErr error sendErr error @@ -114,9 +121,9 @@ func TestProviderByteStreamWrapsProviderAndCallbacks(t *testing.T) { func TestProviderByteStreamWrapsErrors(t *testing.T) { prov := &stubProvider{ - connectErr: errors.New("connect boom"), - sendErr: errors.New("send boom"), - closeErr: errors.New("close boom"), + connectErr: errConnectBoom, + sendErr: errSendBoom, + closeErr: errCloseBoom, } stream := &providerByteStream{provider: prov} @@ -162,7 +169,7 @@ func TestProviderSessionOpenByteStreamAndVideoTrack(t *testing.T) { } func TestProviderVideoTrackWrapsOperations(t *testing.T) { - prov := &stubProvider{canSend: true, addTrackErr: errors.New("track boom")} + prov := &stubProvider{canSend: true, addTrackErr: errTrackBoom} track := &providerVideoTrack{provider: prov} called := false @@ -184,8 +191,8 @@ func TestProviderVideoTrackWrapsOperations(t *testing.T) { func TestProviderVideoTrackWrapsConnectCloseErrors(t *testing.T) { prov := &stubProvider{ - connectErr: errors.New("connect boom"), - closeErr: errors.New("close boom"), + connectErr: errConnectBoom, + closeErr: errCloseBoom, } track := &providerVideoTrack{provider: prov} diff --git a/internal/carrier/carrier.go b/internal/carrier/carrier.go index 9c70261..3dde979 100644 --- a/internal/carrier/carrier.go +++ b/internal/carrier/carrier.go @@ -49,7 +49,7 @@ type Config struct { // Factory creates a new carrier session. type Factory func(ctx context.Context, cfg Config) (Session, error) -var registry = make(map[string]Factory) +var registry = make(map[string]Factory) //nolint:gochecknoglobals // package-level state intentional // Register adds a carrier factory to the registry. func Register(name string, factory Factory) { diff --git a/internal/client/client_test.go b/internal/client/client_test.go index 4c33da1..3aa146b 100644 --- a/internal/client/client_test.go +++ b/internal/client/client_test.go @@ -16,6 +16,8 @@ import ( "github.com/xtaci/smux" ) +var errUnexpectedConnectRequest = errors.New("unexpected connect request") + func TestSetupCipher(t *testing.T) { keyHex := "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" cipher, err := setupCipher(keyHex) @@ -98,7 +100,8 @@ func TestSocks5HandshakeWithAuth(t *testing.T) { t.Fatalf("method selection = %v, want [5 2]", resp) } // Send the auth sub-negotiation: VER(1) + ULEN(1) + USER + PLEN(1) + PASS - authReq := []byte{0x01, 0x04} + authReq := make([]byte, 0, 11) + authReq = append(authReq, 0x01, 0x04) authReq = append(authReq, []byte("user")...) authReq = append(authReq, 0x04) authReq = append(authReq, []byte("pass")...) @@ -141,7 +144,8 @@ func TestSocks5HandshakeAuthRejected(t *testing.T) { t.Fatalf("ReadFull method: %v", err) } // Send wrong credentials - authReq := []byte{0x01, 0x04} + authReq := make([]byte, 0, 12) + authReq = append(authReq, 0x01, 0x04) authReq = append(authReq, []byte("user")...) authReq = append(authReq, 0x05) authReq = append(authReq, []byte("wrong")...) @@ -266,7 +270,8 @@ func TestSocks5RequestDomain(t *testing.T) { }{addr: addr, port: port, err: err} }() - req := []byte{5, 1, 0, 3, 11} + req := make([]byte, 0, 16) + req = append(req, 5, 1, 0, 3, 11) req = append(req, []byte("example.com")...) port := make([]byte, 2) binary.BigEndian.PutUint16(port, 443) @@ -379,6 +384,7 @@ func TestReadSocks5AddrReadErrors(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestSendConnectRequestOverSmux(t *testing.T) { a, b := net.Pipe() defer func() { @@ -411,8 +417,8 @@ func TestSendConnectRequestOverSmux(t *testing.T) { done <- err return } - if req["cmd"] != "connect" || req["clientId"] != "client-1" || req["addr"] != "example.com" { - done <- errors.New("unexpected connect request") + if req["cmd"] != "connect" || req["clientId"] != "client-1" || req["addr"] != "example.com" { //nolint:goconst,lll // test literal, repetition is intentional + done <- errUnexpectedConnectRequest return } _, err = stream.Write([]byte{0x00}) @@ -486,7 +492,7 @@ func (s *closerLinkStub) SetEndedCallback(func(string)) {} func (s *closerLinkStub) WatchConnection(context.Context) {} func (s *closerLinkStub) CanSend() bool { return true } -func TestOnDataWithNilConn(t *testing.T) { +func TestOnDataWithNilConn(_ *testing.T) { c := &Client{} c.onData([]byte("ignored")) } diff --git a/internal/e2e/tunnel_test.go b/internal/e2e/tunnel_test.go index d73cde7..2e14e73 100644 --- a/internal/e2e/tunnel_test.go +++ b/internal/e2e/tunnel_test.go @@ -29,37 +29,46 @@ import ( const testKeyHex = "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" var ( - realE2E = flag.Bool( + errRealE2ENotReady = errors.New("real e2e client did not become ready") + errTunnelDidNotStop = errors.New("tunnel goroutine did not stop") + errRealE2EEchoMismatch = errors.New("real e2e echo payload mismatch") + errSocksUnexpectedReply = errors.New("unexpected SOCKS5 reply") + errSocksUnexpectedHello = errors.New("unexpected SOCKS5 greeting") + errPayloadMismatchOffset = errors.New("payload mismatch at offset") +) + +var ( + realE2E = flag.Bool( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-e2e", false, "run real provider e2e matrix against external WebRTC services", ) - realE2ECarriers = flag.String( + realE2ECarriers = flag.String( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-carriers", "telemost,wbstream", "comma-separated carriers for real e2e", ) - realE2ETransports = flag.String( + realE2ETransports = flag.String( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-transports", "datachannel,videochannel,seichannel,vp8channel", "comma-separated transports for real e2e", ) - realE2EJazzRoom = flag.String( + realE2EJazzRoom = flag.String( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-jazz-room", "", "SaluteJazz room for real e2e, format roomID:password; autogenerated when empty", ) - realE2ETelemostRoom = flag.String( + realE2ETelemostRoom = flag.String( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-telemost-room", "41514917109506", "Telemost room URL or id for real e2e", ) - realE2EWBStreamRoom = flag.String( + realE2EWBStreamRoom = flag.String( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-wbstream-room", "", "WB Stream room id for real e2e; autogenerated when empty", ) - realE2ETimeout = flag.Duration( + realE2ETimeout = flag.Duration( //nolint:gochecknoglobals // package-level state intentional "olcrtc.real-timeout", 90*time.Second, "timeout per real e2e provider/transport case", @@ -288,7 +297,7 @@ func registerMemoryCarrier(t *testing.T) (string, *memoryRoom) { return name, room } -func registerMemoryCarrierAs(t *testing.T, name string) *memoryRoom { +func registerMemoryCarrierAs(t *testing.T, name string) { t.Helper() room := &memoryRoom{streams: make(map[*memoryStream]struct{})} @@ -299,11 +308,10 @@ func registerMemoryCarrierAs(t *testing.T, name string) *memoryRoom { room.mu.Unlock() return &memorySession{stream: stream}, nil }) - return room } func builtInCarrierNames() []string { - return []string{"jazz", "telemost", "wbstream"} + return []string{"jazz", "telemost", "wbstream"} //nolint:goconst // test literal, repetition is intentional } func builtInTransportNames() []string { @@ -340,6 +348,7 @@ func splitTestList(value string) []string { return items } +//nolint:cyclop // table-driven test naturally has many branches func realRoomURL(ctx context.Context, t *testing.T, carrierName string) string { t.Helper() @@ -441,7 +450,8 @@ func validLinkConfig(carrierName, transportName string) link.Config { func startEchoServer(t *testing.T) string { t.Helper() - ln, err := net.Listen("tcp4", "127.0.0.1:0") + var lc net.ListenConfig + ln, err := lc.Listen(context.Background(), "tcp4", "127.0.0.1:0") if err != nil { t.Fatalf("listen echo: %v", err) } @@ -463,9 +473,10 @@ func startEchoServer(t *testing.T) string { return ln.Addr().String() } -func freeLocalAddr(t *testing.T) string { +func freeLocalAddr(ctx context.Context, t *testing.T) string { t.Helper() - ln, err := net.Listen("tcp4", "127.0.0.1:0") + var lc net.ListenConfig + ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:0") if err != nil { t.Fatalf("reserve local addr: %v", err) } @@ -497,10 +508,9 @@ func startTunnel(t *testing.T, serverClientID, clientClientID string) *tunnelRun t.Helper() carrierName, room := registerMemoryCarrier(t) - socksAddr := freeLocalAddr(t) - ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) + socksAddr := freeLocalAddr(ctx, t) serverErr := make(chan error, 1) go func() { @@ -581,14 +591,14 @@ func startTunnel(t *testing.T, serverClientID, clientClientID string) *tunnelRun } func startRealTunnel( - t *testing.T, ctx context.Context, + t *testing.T, carrierName, transportName, roomURL, serverClientID, clientClientID string, ) (*tunnelRuntime, error) { t.Helper() session.RegisterDefaults() - socksAddr := freeLocalAddr(t) + socksAddr := freeLocalAddr(ctx, t) runCtx, cancel := context.WithCancel(ctx) t.Cleanup(cancel) @@ -680,7 +690,7 @@ func startRealTunnel( return nil, fmt.Errorf("server exited before client ready: %w", err) case <-time.After(*realE2ETimeout): cancel() - return nil, errors.New("real e2e client did not become ready") + return nil, errRealE2ENotReady case <-runCtx.Done(): cancel() return nil, fmt.Errorf("real e2e context ended before ready: %w", runCtx.Err()) @@ -721,7 +731,7 @@ func (r *tunnelRuntime) waitStoppedErr() error { return fmt.Errorf("%s returned error: %w", name, err) } case <-time.After(3 * time.Second): - return fmt.Errorf("%s did not stop", name) + return fmt.Errorf("%w: %s", errTunnelDidNotStop, name) } } return nil @@ -730,7 +740,8 @@ func (r *tunnelRuntime) waitStoppedErr() error { func connectViaSOCKS(t *testing.T, socksAddr, targetAddr string) net.Conn { t.Helper() - conn, err := net.DialTimeout("tcp4", socksAddr, 2*time.Second) + dialer := net.Dialer{Timeout: 2 * time.Second} + conn, err := dialer.DialContext(context.Background(), "tcp4", socksAddr) if err != nil { t.Fatalf("dial socks: %v", err) } @@ -759,10 +770,11 @@ func connectViaSOCKS(t *testing.T, socksAddr, targetAddr string) net.Conn { _ = conn.Close() t.Fatalf("parse target port: %v", err) } - req := []byte{5, 1, 0, 1} + req := make([]byte, 0, 10) + req = append(req, 5, 1, 0, 1) req = append(req, net.ParseIP(host).To4()...) var portBuf [2]byte - binary.BigEndian.PutUint16(portBuf[:], uint16(port)) + binary.BigEndian.PutUint16(portBuf[:], uint16(port)) //nolint:gosec // SOCKS5 port is uint16 by definition req = append(req, portBuf[:]...) if _, err := conn.Write(req); err != nil { _ = conn.Close() @@ -785,7 +797,8 @@ func connectViaSOCKS(t *testing.T, socksAddr, targetAddr string) net.Conn { func connectViaSOCKSExpectFailure(t *testing.T, socksAddr, targetAddr string) []byte { t.Helper() - conn, err := net.DialTimeout("tcp4", socksAddr, 2*time.Second) + dialer := net.Dialer{Timeout: 2 * time.Second} + conn, err := dialer.DialContext(context.Background(), "tcp4", socksAddr) if err != nil { t.Fatalf("dial socks: %v", err) } @@ -807,10 +820,11 @@ func connectViaSOCKSExpectFailure(t *testing.T, socksAddr, targetAddr string) [] if err != nil { t.Fatalf("parse target port: %v", err) } - req := []byte{5, 1, 0, 1} + req := make([]byte, 0, 10) + req = append(req, 5, 1, 0, 1) req = append(req, net.ParseIP(host).To4()...) var portBuf [2]byte - binary.BigEndian.PutUint16(portBuf[:], uint16(port)) + binary.BigEndian.PutUint16(portBuf[:], uint16(port)) //nolint:gosec // SOCKS5 port is uint16 by definition req = append(req, portBuf[:]...) if _, err := conn.Write(req); err != nil { t.Fatalf("write socks connect: %v", err) @@ -898,6 +912,7 @@ func TestDirectLinkConnectsFastProviderTransportMatrix(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestRealProviderTransportMatrix(t *testing.T) { if !*realE2E { t.Skip("real provider e2e disabled; pass -olcrtc.real-e2e with provider room flags") @@ -944,7 +959,7 @@ func runRealE2ECase(t *testing.T, carrierName, transportName, roomURL, echoAddr ctx, cancel := context.WithTimeout(context.Background(), *realE2ETimeout) defer cancel() - rt, err := startRealTunnel(t, ctx, carrierName, transportName, roomURL, "client-1", "client-1") + rt, err := startRealTunnel(ctx, t, carrierName, transportName, roomURL, "client-1", "client-1") if err != nil { return err } @@ -972,7 +987,7 @@ func runRealE2ECase(t *testing.T, carrierName, transportName, roomURL, echoAddr return fmt.Errorf("read real e2e echo: %w", err) } if !bytes.Equal(line, payload) { - return fmt.Errorf("real e2e echo = %q, want %q", line, payload) + return fmt.Errorf("%w: got %q, want %q", errRealE2EEchoMismatch, line, payload) } return nil } @@ -1082,51 +1097,53 @@ func connectViaSOCKSWithin(socksAddr, targetAddr string, timeout time.Duration) } func tryConnectViaSOCKS(socksAddr, targetAddr string) (net.Conn, error) { - conn, err := net.DialTimeout("tcp4", socksAddr, 500*time.Millisecond) + dialer := net.Dialer{Timeout: 500 * time.Millisecond} + conn, err := dialer.DialContext(context.Background(), "tcp4", socksAddr) if err != nil { - return nil, err + return nil, fmt.Errorf("dial socks: %w", err) } if _, err := conn.Write([]byte{5, 1, 0}); err != nil { _ = conn.Close() - return nil, err + return nil, fmt.Errorf("write greeting: %w", err) } greeting := make([]byte, 2) if _, err := io.ReadFull(conn, greeting); err != nil { _ = conn.Close() - return nil, err + return nil, fmt.Errorf("read greeting: %w", err) } if !bytes.Equal(greeting, []byte{5, 0}) { _ = conn.Close() - return nil, fmt.Errorf("unexpected greeting: %v", greeting) + return nil, fmt.Errorf("%w: %v", errSocksUnexpectedHello, greeting) } host, portText, err := net.SplitHostPort(targetAddr) if err != nil { _ = conn.Close() - return nil, err + return nil, fmt.Errorf("split host port: %w", err) } port, err := strconv.Atoi(portText) if err != nil { _ = conn.Close() - return nil, err + return nil, fmt.Errorf("parse port: %w", err) } - req := []byte{5, 1, 0, 1} + req := make([]byte, 0, 10) + req = append(req, 5, 1, 0, 1) req = append(req, net.ParseIP(host).To4()...) var portBuf [2]byte - binary.BigEndian.PutUint16(portBuf[:], uint16(port)) + binary.BigEndian.PutUint16(portBuf[:], uint16(port)) //nolint:gosec // SOCKS5 port is uint16 by definition req = append(req, portBuf[:]...) if _, err := conn.Write(req); err != nil { _ = conn.Close() - return nil, err + return nil, fmt.Errorf("write connect request: %w", err) } reply := make([]byte, 10) if _, err := io.ReadFull(conn, reply); err != nil { _ = conn.Close() - return nil, err + return nil, fmt.Errorf("read connect reply: %w", err) } if !bytes.Equal(reply, []byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) { _ = conn.Close() - return nil, fmt.Errorf("unexpected reply: %v", reply) + return nil, fmt.Errorf("%w: %v", errSocksUnexpectedReply, reply) } return conn, nil } @@ -1175,14 +1192,14 @@ func streamPatternAndVerifyEcho(conn net.Conn, size int64) error { n = int(remaining) } if err := conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil { - return err + return fmt.Errorf("set read deadline: %w", err) } if _, err := io.ReadFull(conn, buf[:n]); err != nil { return fmt.Errorf("read at %d: %w", read, err) } fillPattern(want[:n], read) if !bytes.Equal(buf[:n], want[:n]) { - return fmt.Errorf("payload mismatch at offset %d", read) + return fmt.Errorf("%w %d", errPayloadMismatchOffset, read) } read += int64(n) } diff --git a/internal/link/direct/direct_test.go b/internal/link/direct/direct_test.go index ffe291c..bc1f3f0 100644 --- a/internal/link/direct/direct_test.go +++ b/internal/link/direct/direct_test.go @@ -9,6 +9,13 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/transport" ) +var ( + errDirectBoom = errors.New("boom") + errDirectConnectBoom = errors.New("connect boom") + errDirectSendBoom = errors.New("send boom") + errDirectCloseBoom = errors.New("close boom") +) + type stubTransport struct { connectErr error sendErr error @@ -41,6 +48,7 @@ func (s *stubTransport) WatchConnection(context.Context) { s.watched = true } func (s *stubTransport) CanSend() bool { return s.canSend } func (s *stubTransport) Features() transport.Features { return transport.Features{} } +//nolint:cyclop // table-driven test naturally has many branches func TestNewForwardsConfigAndMethods(t *testing.T) { name := "direct-test-forward" var seen transport.Config @@ -109,7 +117,7 @@ func TestNewForwardsConfigAndMethods(t *testing.T) { func TestNewWrapsFactoryError(t *testing.T) { name := "direct-test-error" transport.Register(name, func(context.Context, transport.Config) (transport.Transport, error) { - return nil, errors.New("boom") + return nil, errDirectBoom }) _, err := New(context.Background(), link.Config{Transport: name}) @@ -120,9 +128,9 @@ func TestNewWrapsFactoryError(t *testing.T) { func TestDirectLinkWrapsTransportErrors(t *testing.T) { ln := &directLink{transport: &stubTransport{ - connectErr: errors.New("connect boom"), - sendErr: errors.New("send boom"), - closeErr: errors.New("close boom"), + connectErr: errDirectConnectBoom, + sendErr: errDirectSendBoom, + closeErr: errDirectCloseBoom, }} if err := ln.Connect(context.Background()); err == nil || err.Error() != "transport connect: connect boom" { diff --git a/internal/link/link.go b/internal/link/link.go index 69f49b1..23606a2 100644 --- a/internal/link/link.go +++ b/internal/link/link.go @@ -55,7 +55,7 @@ type Config struct { // Factory creates a link instance. type Factory func(ctx context.Context, cfg Config) (Link, error) -var registry = make(map[string]Factory) +var registry = make(map[string]Factory) //nolint:gochecknoglobals // package-level state intentional // Register adds a link factory to the registry. func Register(name string, factory Factory) { diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 0f6cb7c..50d611d 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -7,7 +7,7 @@ import ( ) // verboseEnabled controls whether verbose and debug logging is enabled. -var verboseEnabled atomic.Bool +var verboseEnabled atomic.Bool //nolint:gochecknoglobals // package-level state intentional // SetVerbose enables or disables verbose/debug logging. func SetVerbose(enabled bool) { diff --git a/internal/muxconn/conn_test.go b/internal/muxconn/conn_test.go index f628e1f..8df5424 100644 --- a/internal/muxconn/conn_test.go +++ b/internal/muxconn/conn_test.go @@ -12,6 +12,8 @@ import ( cryptopkg "github.com/openlibrecommunity/olcrtc/internal/crypto" ) +var errMuxBoom = errors.New("boom") + type stubLink struct { mu sync.Mutex canSend bool @@ -163,7 +165,7 @@ func TestWriteReturnsErrClosedWhileWaiting(t *testing.T) { func TestWriteWrapsSendError(t *testing.T) { cipher := newTestCipher(t) - conn := New(&stubLink{canSend: true, sendErr: errors.New("boom")}, cipher) + conn := New(&stubLink{canSend: true, sendErr: errMuxBoom}, cipher) _, err := conn.Write([]byte("payload")) if err == nil || err.Error() != "send: boom" { diff --git a/internal/names/names.go b/internal/names/names.go index ba043e5..a4bd723 100644 --- a/internal/names/names.go +++ b/internal/names/names.go @@ -18,8 +18,8 @@ var embeddedNames string var embeddedSurnames string var ( - firstNames = parseEmbedded(embeddedNames) - lastNames = parseEmbedded(embeddedSurnames) + firstNames = parseEmbedded(embeddedNames) //nolint:gochecknoglobals // package-level state intentional + lastNames = parseEmbedded(embeddedSurnames) //nolint:gochecknoglobals // package-level state intentional ) func parseEmbedded(raw string) []string { @@ -35,7 +35,7 @@ func parseEmbedded(raw string) []string { } func loadNames(path string) ([]string, error) { - file, err := os.Open(path) + file, err := os.Open(path) //nolint:gosec // G304: opens internal asset bundled with the binary if err != nil { return nil, fmt.Errorf("open names file %q: %w", path, err) } diff --git a/internal/names/names_test.go b/internal/names/names_test.go index b8cdb38..e360b26 100644 --- a/internal/names/names_test.go +++ b/internal/names/names_test.go @@ -75,7 +75,7 @@ func TestGenerateFallsBackWhenNamesEmpty(t *testing.T) { } func TestRandomIndexBounds(t *testing.T) { - for i := 0; i < 20; i++ { + for range 20 { got := randomIndex(2) if got < 0 || got > 1 { t.Fatalf("randomIndex(2) = %d, out of range", got) diff --git a/internal/protect/protect.go b/internal/protect/protect.go index 88bd23d..29bc277 100644 --- a/internal/protect/protect.go +++ b/internal/protect/protect.go @@ -12,7 +12,7 @@ import ( // Protector is called with a socket file descriptor before connect. // On Android, this calls VpnService.protect(fd) to bypass VPN routing. -var Protector func(fd int) bool +var Protector func(fd int) bool //nolint:gochecknoglobals // package-level state intentional func controlFunc(network, _ string, c syscall.RawConn) error { if Protector == nil { diff --git a/internal/protect/protect_test.go b/internal/protect/protect_test.go index 0cecf50..515f82d 100644 --- a/internal/protect/protect_test.go +++ b/internal/protect/protect_test.go @@ -10,6 +10,8 @@ import ( "time" ) +var errProtectBoom = errors.New("boom") + type rawConnStub struct { controlFn func(func(uintptr)) error } @@ -67,13 +69,14 @@ func TestControlFuncWrapsControlError(t *testing.T) { t.Cleanup(func() { Protector = old }) err := controlFunc("tcp4", "", rawConnStub{ - controlFn: func(func(uintptr)) error { return errors.New("boom") }, + controlFn: func(func(uintptr)) error { return errProtectBoom }, }) if err == nil || err.Error() != "control failed: boom" { t.Fatalf("controlFunc() error = %v", err) } } +//nolint:cyclop // table-driven test naturally has many branches func TestNewDialerAndHTTPClient(t *testing.T) { dialer := NewDialer() if dialer.Timeout != 10*time.Second || dialer.KeepAlive != 30*time.Second || dialer.Control == nil { @@ -93,7 +96,8 @@ func TestNewDialerAndHTTPClient(t *testing.T) { } func TestDialContextAndProxyDialer(t *testing.T) { - ln, err := net.Listen("tcp4", "127.0.0.1:0") + var lc net.ListenConfig + ln, err := lc.Listen(context.Background(), "tcp4", "127.0.0.1:0") if err != nil { t.Fatalf("Listen() error = %v", err) } diff --git a/internal/provider/jazz/api.go b/internal/provider/jazz/api.go index 6c291e0..a4250b1 100644 --- a/internal/provider/jazz/api.go +++ b/internal/provider/jazz/api.go @@ -20,7 +20,7 @@ const ( contentTypeJSON = "application/json" ) -var apiBase = "https://bk.salutejazz.ru" +var apiBase = "https://bk.salutejazz.ru" //nolint:gochecknoglobals // package-level state intentional // RoomInfo contains connection details for a SaluteJazz room. type RoomInfo struct { diff --git a/internal/provider/jazz/api_test.go b/internal/provider/jazz/api_test.go index 65ac7c3..4bdf683 100644 --- a/internal/provider/jazz/api_test.go +++ b/internal/provider/jazz/api_test.go @@ -10,7 +10,7 @@ import ( "testing" ) -func withJazzAPIServer(t *testing.T, h http.Handler) string { +func withJazzAPIServer(t *testing.T, h http.Handler) { t.Helper() old := apiBase srv := httptest.NewServer(h) @@ -19,25 +19,25 @@ func withJazzAPIServer(t *testing.T, h http.Handler) string { srv.Close() }) apiBase = srv.URL - return srv.URL } +//nolint:cyclop // table-driven test naturally has many branches func TestCreateMeetingAndPreconnect(t *testing.T) { withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get(headerAuthType) != authTypeAnonymous { t.Fatalf("missing auth header: %v", r.Header) } switch r.URL.Path { - case "/room/create-meeting": + case "/room/create-meeting": //nolint:goconst // test literal, repetition is intentional if r.Method != http.MethodPost { t.Fatalf("create method = %s", r.Method) } - _ = json.NewEncoder(w).Encode(createResponse{RoomID: "room-1", Password: "pass"}) + _ = json.NewEncoder(w).Encode(createResponse{RoomID: "room-1", Password: "pass"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape case "/room/room-1/preconnect": if r.Method != http.MethodPost { t.Fatalf("preconnect method = %s", r.Method) } - _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) + _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) //nolint:goconst,lll // test literal, repetition is intentional default: http.NotFound(w, r) } @@ -64,11 +64,12 @@ func TestCreateMeetingAndPreconnect(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestCreateRoomAndJoinRoom(t *testing.T) { withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/room/create-meeting": - _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) + _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) //nolint:goconst,gosec,lll // test literal; G117 is a false positive for test fixtures case "/room/new-room/preconnect", "/room/existing/preconnect": _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) default: @@ -115,7 +116,7 @@ func TestNewPeerUsesRoomAPI(t *testing.T) { withJazzAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/room/create-meeting": - _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) + _ = json.NewEncoder(w).Encode(createResponse{RoomID: "new-room", Password: "new-pass"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape case "/room/new-room/preconnect", "/room/existing/preconnect": _ = json.NewEncoder(w).Encode(map[string]string{"connectorUrl": "wss://connector"}) default: diff --git a/internal/provider/jazz/datapacket.go b/internal/provider/jazz/datapacket.go index 4f276f1..7614fb8 100644 --- a/internal/provider/jazz/datapacket.go +++ b/internal/provider/jazz/datapacket.go @@ -16,7 +16,7 @@ func encodeVarint(value uint64) []byte { } func encodeField(fieldNumber int, wireType int, data []byte) []byte { - tag := encodeVarint(uint64(fieldNumber)<<3 | uint64(wireType)) + tag := encodeVarint(uint64(fieldNumber)<<3 | uint64(wireType)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic switch wireType { case 2: length := encodeVarint(uint64(len(data))) @@ -101,12 +101,12 @@ func handleWireType(reader *byteReader, wireType int, dataLen int) ([]byte, bool if err != nil { return nil, false } - if length > uint64(dataLen)-uint64(reader.pos) { + if length > uint64(dataLen)-uint64(reader.pos) { //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic return nil, false } fieldData := make([]byte, length) n, err := reader.Read(fieldData) - if err != nil || uint64(n) != length { + if err != nil || uint64(n) != length { //nolint:gosec // G115: bounded conversion verified by surrounding logic return nil, false } return fieldData, true diff --git a/internal/provider/jazz/peer_helpers_test.go b/internal/provider/jazz/peer_helpers_test.go index 24729fc..ffa86e3 100644 --- a/internal/provider/jazz/peer_helpers_test.go +++ b/internal/provider/jazz/peer_helpers_test.go @@ -9,6 +9,7 @@ import ( "github.com/pion/webrtc/v4" ) +//nolint:cyclop // table-driven test naturally has many branches func TestPeerStateHelpers(t *testing.T) { p := &Peer{ reconnectCh: make(chan struct{}, 1), diff --git a/internal/provider/telemost/api.go b/internal/provider/telemost/api.go index 32118e7..2afd298 100644 --- a/internal/provider/telemost/api.go +++ b/internal/provider/telemost/api.go @@ -13,10 +13,15 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/protect" ) +//nolint:gochecknoglobals // overridable base URL for tests var apiBase = "https://cloud-api.yandex.ru/telemost_front/v2/telemost" +// ErrAPI marks failures returned by the Telemost HTTP API. var ErrAPI = errors.New("api error") +// ConnectionInfo describes the connection metadata returned by the Telemost API. +// +//nolint:tagliatelle // wire format dictated by the upstream Telemost API type ConnectionInfo struct { RoomID string `json:"room_id"` PeerID string `json:"peer_id"` @@ -26,6 +31,7 @@ type ConnectionInfo struct { } `json:"client_configuration"` } +// GetConnectionInfo fetches connection metadata for the given Telemost room URL. func GetConnectionInfo(ctx context.Context, roomURL, displayName string) (*ConnectionInfo, error) { u := fmt.Sprintf("%s/conferences/%s/connection", apiBase, url.QueryEscape(roomURL)) diff --git a/internal/provider/telemost/api_test.go b/internal/provider/telemost/api_test.go index d072cfc..1650e60 100644 --- a/internal/provider/telemost/api_test.go +++ b/internal/provider/telemost/api_test.go @@ -33,9 +33,9 @@ func TestGetConnectionInfo(t *testing.T) { t.Fatalf("display_name query = %q", r.URL.Query().Get("display_name")) } _ = json.NewEncoder(w).Encode(ConnectionInfo{ - RoomID: "room", - PeerID: "peer-id", - Credentials: "creds", + RoomID: "room", //nolint:goconst // test literal, repetition is intentional + PeerID: "peer-id", //nolint:goconst // test literal, repetition is intentional + Credentials: "creds", //nolint:goconst // test literal, repetition is intentional }) })) @@ -49,14 +49,14 @@ func TestGetConnectionInfo(t *testing.T) { } func TestGetConnectionInfoErrors(t *testing.T) { - withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { http.Error(w, "bad", http.StatusForbidden) })) if _, err := GetConnectionInfo(context.Background(), "room", "peer"); !errors.Is(err, ErrAPI) { t.Fatalf("GetConnectionInfo() error = %v, want %v", err, ErrAPI) } - withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("{")) })) if _, err := GetConnectionInfo(context.Background(), "room", "peer"); err == nil { @@ -65,7 +65,7 @@ func TestGetConnectionInfoErrors(t *testing.T) { } func TestTelemostNewPeerUsesConnectionInfo(t *testing.T) { - withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + withTelemostAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _ = json.NewEncoder(w).Encode(ConnectionInfo{ RoomID: "room", PeerID: "peer-id", diff --git a/internal/provider/telemost/peer.go b/internal/provider/telemost/peer.go index eabb1c9..0a16591 100644 --- a/internal/provider/telemost/peer.go +++ b/internal/provider/telemost/peer.go @@ -1469,7 +1469,7 @@ func (p *Peer) calculateDelay() time.Duration { if maxDelay <= minDelay { return minDelay } - return minDelay + time.Duration(rand.Int64N(int64(maxDelay-minDelay))) + return minDelay + time.Duration(rand.Int64N(int64(maxDelay-minDelay))) //nolint:gosec,lll // G404: non-cryptographic shaping randomness } // CanSend checks if data can be sent. diff --git a/internal/provider/telemost/peer_helpers_test.go b/internal/provider/telemost/peer_helpers_test.go index 2c9d6f4..de892e4 100644 --- a/internal/provider/telemost/peer_helpers_test.go +++ b/internal/provider/telemost/peer_helpers_test.go @@ -34,7 +34,7 @@ func TestTrafficShapeAndDelay(t *testing.T) { } p.SetTrafficShape(TrafficShape{MaxMessageSize: 10, MinDelay: time.Millisecond, MaxDelay: 4 * time.Millisecond}) - for i := 0; i < 20; i++ { + for range 20 { got := p.calculateDelay() if got < time.Millisecond || got >= 4*time.Millisecond { t.Fatalf("calculateDelay() = %v, out of range", got) @@ -50,7 +50,7 @@ func TestICEParsingFiltersTURN(t *testing.T) { t.Fatal("isNonTURNURL rejected STUN URL") } - urls := parseICEURLs(map[string]interface{}{"urls": []interface{}{"turn:x", "stun:a", 123, "turns:y"}}) + urls := parseICEURLs(map[string]interface{}{"urls": []interface{}{"turn:x", "stun:a", 123, "turns:y"}}) //nolint:goconst,lll // test literal, repetition is intentional if len(urls) != 1 || urls[0] != "stun:a" { t.Fatalf("parseICEURLs(interface) = %v, want [stun:a]", urls) } @@ -85,7 +85,7 @@ func TestParseICEServer(t *testing.T) { func TestConferenceEndParsing(t *testing.T) { for _, msg := range []map[string]interface{}{ {"conferenceClosed": true}, - {"conference": map[string]interface{}{"state": "ENDED"}}, + {"conference": map[string]interface{}{"state": "ENDED"}}, //nolint:goconst // test literal, repetition is intentional {"conferenceState": map[string]interface{}{"state": "terminated"}}, } { if !isConferenceEndMessage(msg) { @@ -106,6 +106,7 @@ func TestConferenceEndParsing(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestPeerSmallStateHelpers(t *testing.T) { p := &Peer{ reconnectCh: make(chan struct{}, 1), @@ -169,7 +170,7 @@ func TestTelemetryCfgParsing(t *testing.T) { t.Fatal("parseTelemetryCfg() accepted missing config") } if _, _, ok := parseTelemetryCfg(map[string]interface{}{ - "telemetryConfiguration": map[string]interface{}{}, + "telemetryConfiguration": map[string]interface{}{}, //nolint:goconst // test literal, repetition is intentional }); ok { t.Fatal("parseTelemetryCfg() accepted missing endpoint") } diff --git a/internal/provider/telemost/provider_test.go b/internal/provider/telemost/provider_test.go index d70c4e4..e29d008 100644 --- a/internal/provider/telemost/provider_test.go +++ b/internal/provider/telemost/provider_test.go @@ -8,6 +8,7 @@ import ( "github.com/pion/webrtc/v4" ) +//nolint:cyclop // table-driven test naturally has many branches func TestTelemostProviderForwardsPeerMethods(t *testing.T) { peer := &Peer{ reconnectCh: make(chan struct{}, 1), diff --git a/internal/provider/telemost/state_helpers_test.go b/internal/provider/telemost/state_helpers_test.go index f072429..08f9362 100644 --- a/internal/provider/telemost/state_helpers_test.go +++ b/internal/provider/telemost/state_helpers_test.go @@ -5,6 +5,7 @@ import ( "time" ) +//nolint:cyclop // table-driven test naturally has many branches func TestSessionReconnectAndEndedHelpers(t *testing.T) { p := &Peer{ reconnectCh: make(chan struct{}, 2), diff --git a/internal/provider/wbstream/api.go b/internal/provider/wbstream/api.go index 540745e..7b991a1 100644 --- a/internal/provider/wbstream/api.go +++ b/internal/provider/wbstream/api.go @@ -12,7 +12,7 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/protect" ) -var apiBase = "https://stream.wb.ru" +var apiBase = "https://stream.wb.ru" //nolint:gochecknoglobals // package-level state intentional var ( errGuestRegister = errors.New("guest register failed") diff --git a/internal/provider/wbstream/api_test.go b/internal/provider/wbstream/api_test.go index 99ef8b1..1ec6b26 100644 --- a/internal/provider/wbstream/api_test.go +++ b/internal/provider/wbstream/api_test.go @@ -20,6 +20,7 @@ func withWBAPIServer(t *testing.T, h http.Handler) { apiBase = srv.URL } +//nolint:cyclop // table-driven test naturally has many branches func TestWBStreamAPIHappyPath(t *testing.T) { withWBAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { @@ -27,20 +28,20 @@ func TestWBStreamAPIHappyPath(t *testing.T) { if r.Method != http.MethodPost { t.Fatalf("guest method = %s", r.Method) } - _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) + _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) //nolint:goconst,gosec,lll // test literal; G117 is a false positive for test fixtures case "/api-room/api/v2/room": if r.Header.Get("Authorization") != "Bearer access" { t.Fatalf("room auth = %q", r.Header.Get("Authorization")) } w.WriteHeader(http.StatusCreated) - _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "room"}) + _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "room"}) //nolint:goconst,lll // test literal, repetition is intentional case "/api-room/api/v1/room/room/join": w.WriteHeader(http.StatusOK) case "/api-room-manager/api/v1/room/room/token": if r.URL.Query().Get("displayName") != "peer" { t.Fatalf("displayName query = %q", r.URL.Query().Get("displayName")) } - _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) + _ = json.NewEncoder(w).Encode(tokenResponse{RoomToken: "token"}) //nolint:goconst,lll // test literal, repetition is intentional default: http.NotFound(w, r) } @@ -75,7 +76,7 @@ func TestWBStreamAPIHappyPath(t *testing.T) { } func TestWBStreamAPIErrors(t *testing.T) { - withWBAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + withWBAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { http.Error(w, "bad", http.StatusBadGateway) })) @@ -97,7 +98,7 @@ func TestWBStreamGetRoomToken(t *testing.T) { withWBAPIServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/auth/api/v1/auth/user/guest-register": - _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) + _ = json.NewEncoder(w).Encode(guestRegisterResponse{AccessToken: "access"}) //nolint:gosec,lll // G117: test-only struct mirroring upstream API shape case "/api-room/api/v2/room": _ = json.NewEncoder(w).Encode(createRoomResponse{RoomID: "created"}) case "/api-room/api/v1/room/created/join": diff --git a/internal/provider/wbstream/peer_test.go b/internal/provider/wbstream/peer_test.go index 17a7df4..e9715d5 100644 --- a/internal/provider/wbstream/peer_test.go +++ b/internal/provider/wbstream/peer_test.go @@ -13,7 +13,7 @@ func TestNewPeerAndSimpleAccessors(t *testing.T) { if err != nil { t.Fatalf("NewPeer() error = %v", err) } - if p.roomURL != "room" || p.name != "name" || p.sendQueue == nil || p.done == nil { + if p.roomURL != "room" || p.name != "name" || p.sendQueue == nil || p.done == nil { //nolint:goconst,lll // test literal, repetition is intentional t.Fatalf("NewPeer() = %+v", p) } if p.GetSendQueue() != p.sendQueue { diff --git a/internal/provider/wbstream/provider_test.go b/internal/provider/wbstream/provider_test.go index f33f6f7..fe16e24 100644 --- a/internal/provider/wbstream/provider_test.go +++ b/internal/provider/wbstream/provider_test.go @@ -8,6 +8,7 @@ import ( "github.com/pion/webrtc/v4" ) +//nolint:cyclop // table-driven test naturally has many branches func TestWBStreamProviderForwardsPeerMethods(t *testing.T) { peer, err := NewPeer(context.Background(), "room", "name", nil) if err != nil { diff --git a/internal/server/server.go b/internal/server/server.go index c83c94b..51e96f3 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -481,7 +481,7 @@ func (s *Server) socks5Connect(conn net.Conn, targetAddr string, targetPort int) req := make([]byte, 0, 7+addrLen) req = append(req, 5, 1, 0, 3, byte(addrLen)) req = append(req, []byte(targetAddr)...) - req = append(req, byte(targetPort>>8), byte(targetPort)) + req = append(req, byte(targetPort>>8), byte(targetPort)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic if _, err := conn.Write(req); err != nil { return fmt.Errorf("failed to write socks5 connect req: %w", err) diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 3717d9d..26dbd67 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -48,8 +48,8 @@ func TestSmuxConfig(t *testing.T) { func TestParseConnectRequest(t *testing.T) { buf, err := json.Marshal(ConnectRequest{ Cmd: "connect", - ClientID: "client-1", - Addr: "example.com", + ClientID: "client-1", //nolint:goconst // test literal, repetition is intentional + Addr: "example.com", //nolint:goconst // test literal, repetition is intentional Port: 443, }) if err != nil { @@ -82,6 +82,7 @@ func TestAuthorizeRequest(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestSocks5ConnectSuccess(t *testing.T) { s := &Server{} server, client := net.Pipe() @@ -191,7 +192,7 @@ func TestSetupResolver(t *testing.T) { } } -func TestOnDataWithNilConn(t *testing.T) { +func TestOnDataWithNilConn(_ *testing.T) { s := &Server{} s.onData([]byte("ignored")) } @@ -227,7 +228,8 @@ func TestShutdownClosesLinkAndConn(t *testing.T) { } func TestDialWithoutProxy(t *testing.T) { - ln, err := net.Listen("tcp4", "127.0.0.1:0") + var lc net.ListenConfig + ln, err := lc.Listen(context.Background(), "tcp4", "127.0.0.1:0") if err != nil { t.Fatalf("Listen() error = %v", err) } @@ -242,7 +244,10 @@ func TestDialWithoutProxy(t *testing.T) { } }() - tcpAddr := ln.Addr().(*net.TCPAddr) + tcpAddr, ok := ln.Addr().(*net.TCPAddr) + if !ok { + t.Fatalf("listener addr type = %T, want *net.TCPAddr", ln.Addr()) + } s := &Server{resolver: net.DefaultResolver} conn, err := s.dial(ConnectRequest{Addr: "127.0.0.1", Port: tcpAddr.Port}) if err != nil { @@ -254,7 +259,7 @@ func TestDialWithoutProxy(t *testing.T) { func TestDialProxyError(t *testing.T) { s := &Server{socksProxyAddr: "127.0.0.1", socksProxyPort: 1} - if _, err := s.dial(ConnectRequest{Addr: "example.com", Port: 443}); err == nil || !strings.Contains(err.Error(), "failed to dial proxy") { + if _, err := s.dial(ConnectRequest{Addr: "example.com", Port: 443}); err == nil || !strings.Contains(err.Error(), "failed to dial proxy") { //nolint:lll // long test description t.Fatalf("dial() error = %v", err) } } diff --git a/internal/transport/datachannel/transport_test.go b/internal/transport/datachannel/transport_test.go index 68d9efa..1f4e4f7 100644 --- a/internal/transport/datachannel/transport_test.go +++ b/internal/transport/datachannel/transport_test.go @@ -9,6 +9,14 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/transport" ) +var ( + errDCBoom = errors.New("boom") + errDCOpenBoom = errors.New("open boom") + errDCConnectBoom = errors.New("connect boom") + errDCSendBoom = errors.New("send boom") + errDCCloseBoom = errors.New("close boom") +) + type stubSession struct { stream carrier.ByteStream streamErr error @@ -54,6 +62,7 @@ func (s *stubByteStream) SetEndedCallback(cb func(string)) { s.endedCB = cb } func (s *stubByteStream) WatchConnection(context.Context) { s.watched = true } func (s *stubByteStream) CanSend() bool { return s.canSend } +//nolint:cyclop // table-driven test naturally has many branches func TestNewAndFeatures(t *testing.T) { stream := &stubByteStream{canSend: true} carrier.Register("datachannel-test-new-and-features", func(context.Context, carrier.Config) (carrier.Session, error) { @@ -89,7 +98,7 @@ func TestNewAndFeatures(t *testing.T) { } features := tr.Features() - if !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize != defaultMaxPayloadSize { + if !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize != defaultMaxPayloadSize { //nolint:lll // long test description t.Fatalf("Features() = %+v", features) } if err := tr.Close(); err != nil { @@ -99,32 +108,32 @@ func TestNewAndFeatures(t *testing.T) { func TestNewErrorPaths(t *testing.T) { carrier.Register("datachannel-fail-create", func(context.Context, carrier.Config) (carrier.Session, error) { - return nil, errors.New("boom") + return nil, errDCBoom }) - if _, err := New(context.Background(), transport.Config{Carrier: "datachannel-fail-create"}); err == nil || err.Error() != "create carrier transport: boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "datachannel-fail-create"}); err == nil || err.Error() != "create carrier transport: boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } carrier.Register("datachannel-no-stream", func(context.Context, carrier.Config) (carrier.Session, error) { return &nonByteStreamSession{}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "datachannel-no-stream"}); !errors.Is(err, carrier.ErrByteStreamUnsupported) { + if _, err := New(context.Background(), transport.Config{Carrier: "datachannel-no-stream"}); !errors.Is(err, carrier.ErrByteStreamUnsupported) { //nolint:lll // long test description t.Fatalf("New() error = %v, want %v", err, carrier.ErrByteStreamUnsupported) } carrier.Register("datachannel-open-stream-fails", func(context.Context, carrier.Config) (carrier.Session, error) { - return &stubSession{streamErr: errors.New("open boom")}, nil + return &stubSession{streamErr: errDCOpenBoom}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "datachannel-open-stream-fails"}); err == nil || err.Error() != "open byte stream: open boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "datachannel-open-stream-fails"}); err == nil || err.Error() != "open byte stream: open boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } } func TestStreamTransportWrapsErrors(t *testing.T) { tr := &streamTransport{stream: &stubByteStream{ - connectErr: errors.New("connect boom"), - sendErr: errors.New("send boom"), - closeErr: errors.New("close boom"), + connectErr: errDCConnectBoom, + sendErr: errDCSendBoom, + closeErr: errDCCloseBoom, }} if err := tr.Connect(context.Background()); err == nil || err.Error() != "stream connect: connect boom" { diff --git a/internal/transport/seichannel/h264.go b/internal/transport/seichannel/h264.go index b44e93a..8434a02 100644 --- a/internal/transport/seichannel/h264.go +++ b/internal/transport/seichannel/h264.go @@ -24,15 +24,18 @@ var ( // ErrSEIValueTruncated is returned when reading a SEI length-value runs past the buffer. ErrSEIValueTruncated = errors.New("sei value truncated") - videoSEIUUID = [16]byte{ + videoSEIUUID = [16]byte{ //nolint:gochecknoglobals // package-level state intentional 0x5d, 0xc0, 0x3b, 0xa8, 0x45, 0x0f, 0x4b, 0x55, 0x9a, 0x77, 0x1f, 0x91, 0x6c, 0x5b, 0x07, 0x39, } + //nolint:gochecknoglobals // hardcoded H264 constants baseSPS = mustDecodeHex("6742c00addec0440000003004000000300a3c489e0") + //nolint:gochecknoglobals // hardcoded H264 constants basePPS = mustDecodeHex("68ce0fc8") + //nolint:gochecknoglobals // hardcoded H264 constants baseIDR = mustDecodeHex("6588843a2628000902e0") ) @@ -142,10 +145,11 @@ func appendSEIValue(dst []byte, value int) []byte { dst = append(dst, 0xff) value -= 0xff } - return append(dst, byte(value)) + return append(dst, byte(value)) //nolint:gosec // G115: bounded conversion verified by surrounding logic } -func consumeSEIValue(data []byte, pos int) (value, next int, err error) { +func consumeSEIValue(data []byte, pos int) (int, int, error) { + value := 0 for { if pos >= len(data) { return 0, pos, ErrSEIValueTruncated @@ -196,7 +200,6 @@ func unescapeRBSP(rbsp []byte) []byte { func mustDecodeHex(value string) []byte { data, err := hex.DecodeString(value) if err != nil { - //nolint:forbidigo // hardcoded constant; failure indicates a corrupt binary panic(errors.Join(ErrInvalidH264Constant, err)) } return data diff --git a/internal/transport/seichannel/inbound_test.go b/internal/transport/seichannel/inbound_test.go index 31b54ae..96e6e13 100644 --- a/internal/transport/seichannel/inbound_test.go +++ b/internal/transport/seichannel/inbound_test.go @@ -21,7 +21,7 @@ func TestInboundAssemblyAndAck(t *testing.T) { typ: frameTypeData, seq: 1, crc: crc, - totalLen: uint32(len(payload)), + totalLen: uint32(len(payload)), //nolint:gosec // G115: bounded conversion verified by surrounding logic fragIdx: 1, fragTotal: 2, payload: []byte(" world"), @@ -34,7 +34,7 @@ func TestInboundAssemblyAndAck(t *testing.T) { typ: frameTypeData, seq: 1, crc: crc, - totalLen: uint32(len(payload)), + totalLen: uint32(len(payload)), //nolint:gosec // G115: bounded conversion verified by surrounding logic fragIdx: 0, fragTotal: 2, payload: []byte("hello"), @@ -57,7 +57,7 @@ func TestInboundAssemblyAndAck(t *testing.T) { typ: frameTypeData, seq: 1, crc: crc, - totalLen: uint32(len(payload)), + totalLen: uint32(len(payload)), //nolint:gosec // G115: bounded conversion verified by surrounding logic fragIdx: 0, fragTotal: 2, payload: []byte("hello"), diff --git a/internal/transport/seichannel/transport.go b/internal/transport/seichannel/transport.go index 8a53584..c1b8ac7 100644 --- a/internal/transport/seichannel/transport.go +++ b/internal/transport/seichannel/transport.go @@ -352,11 +352,9 @@ func (p *streamTransport) writeBatch(idle []byte) bool { if i > 0 { return true } - //nolint:errcheck,gosec // best-effort idle keepalive frame _ = p.track.WriteSample(media.Sample{Data: idle, Duration: frameInterval}) return true } - //nolint:errcheck,gosec // best-effort sample write _ = p.track.WriteSample(media.Sample{Data: buildVideoAccessUnit(payload), Duration: frameInterval}) } return true @@ -474,7 +472,7 @@ func (p *streamTransport) assembleMessage(msg *inboundMessage) []byte { for _, frag := range msg.frags { data = append(data, frag...) } - if uint32(len(data)) > msg.totalLen { + if uint32(len(data)) > msg.totalLen { //nolint:gosec // G115: bounded conversion verified by surrounding logic data = data[:msg.totalLen] } return data @@ -515,7 +513,6 @@ func (p *streamTransport) handleInboundFrame(frame transportFrame) { } func (p *streamTransport) sendAck(seq, crc uint32) { - //nolint:dogsled,errcheck // ack delivery is best-effort _ = p.enqueueFrame(encodeAckFrame(seq, crc), true) } @@ -558,9 +555,9 @@ func encodeDataFrame(seq, crc uint32, totalLen, fragIdx, fragTotal int, payload out[5] = frameTypeData binary.BigEndian.PutUint32(out[6:10], seq) binary.BigEndian.PutUint32(out[10:14], crc) - binary.BigEndian.PutUint32(out[14:18], uint32(totalLen)) - binary.BigEndian.PutUint16(out[18:20], uint16(fragIdx)) - binary.BigEndian.PutUint16(out[20:22], uint16(fragTotal)) + binary.BigEndian.PutUint32(out[14:18], uint32(totalLen)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic + binary.BigEndian.PutUint16(out[18:20], uint16(fragIdx)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic + binary.BigEndian.PutUint16(out[20:22], uint16(fragTotal)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic copy(out[22:], payload) return out } diff --git a/internal/transport/seichannel/transport_unit_test.go b/internal/transport/seichannel/transport_unit_test.go index 0ade1fe..00abf58 100644 --- a/internal/transport/seichannel/transport_unit_test.go +++ b/internal/transport/seichannel/transport_unit_test.go @@ -65,6 +65,7 @@ type nonVideoSession struct{} func (s *nonVideoSession) Capabilities() carrier.Capabilities { return carrier.Capabilities{} } +//nolint:cyclop // table-driven test naturally has many branches func TestNewConnectCallbacksAndFeatures(t *testing.T) { stream := &fakeVideoStream{canSend: true} name := "seichannel-unit-new" @@ -105,7 +106,7 @@ func TestNewConnectCallbacksAndFeatures(t *testing.T) { if !tr.CanSend() { t.Fatal("CanSend() = false, want true") } - if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { + if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { //nolint:lll // long test description t.Fatalf("Features() = %+v", features) } if tr.fragmentSize != 512 || tr.batchSize != 3 || tr.frameInterval != 25*time.Millisecond || @@ -122,21 +123,21 @@ func TestNewErrorPaths(t *testing.T) { carrier.Register("seichannel-create-fails", func(context.Context, carrier.Config) (carrier.Session, error) { return nil, errBoom }) - if _, err := New(context.Background(), transport.Config{Carrier: "seichannel-create-fails"}); err == nil || err.Error() != "create carrier transport: boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "seichannel-create-fails"}); err == nil || err.Error() != "create carrier transport: boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } carrier.Register("seichannel-no-video", func(context.Context, carrier.Config) (carrier.Session, error) { return &nonVideoSession{}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "seichannel-no-video"}); !errors.Is(err, ErrVideoTrackUnsupported) { + if _, err := New(context.Background(), transport.Config{Carrier: "seichannel-no-video"}); !errors.Is(err, ErrVideoTrackUnsupported) { //nolint:lll // long test description t.Fatalf("New() error = %v, want %v", err, ErrVideoTrackUnsupported) } carrier.Register("seichannel-open-fails", func(context.Context, carrier.Config) (carrier.Session, error) { return &fakeVideoSession{err: errOpenBoom}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "seichannel-open-fails"}); err == nil || err.Error() != "open video track: open boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "seichannel-open-fails"}); err == nil || err.Error() != "open video track: open boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } } diff --git a/internal/transport/transport.go b/internal/transport/transport.go index 7c36473..3061526 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -63,7 +63,7 @@ type Config struct { // Factory creates a transport instance. type Factory func(ctx context.Context, cfg Config) (Transport, error) -var registry = make(map[string]Factory) +var registry = make(map[string]Factory) //nolint:gochecknoglobals // package-level state intentional // Register adds a transport factory to the registry. func Register(name string, factory Factory) { diff --git a/internal/transport/videochannel/ffmpeg.go b/internal/transport/videochannel/ffmpeg.go index 53b13dc..4fe2e9a 100644 --- a/internal/transport/videochannel/ffmpeg.go +++ b/internal/transport/videochannel/ffmpeg.go @@ -202,7 +202,7 @@ func newFFmpegEncoder( vcodec := resolveEncoderCodec(spec, hw) args := buildEncoderArgs(spec, vcodec, width, height, fps, bitrate) - cmd := exec.CommandContext(ctx, "ffmpeg", args...) + cmd := exec.CommandContext(ctx, "ffmpeg", args...) //nolint:gosec,lll // G204: ffmpeg path is operator-controlled config, not user input stdin, err := cmd.StdinPipe() if err != nil { return nil, fmt.Errorf("encoder stdin: %w", err) @@ -404,7 +404,7 @@ func newFFmpegDecoder( decoderName := resolveDecoderName(spec, hw) args := buildDecoderArgs(spec, decoderName, width, height, "gray") - cmd := exec.CommandContext(ctx, "ffmpeg", args...) + cmd := exec.CommandContext(ctx, "ffmpeg", args...) //nolint:gosec,lll // G204: ffmpeg path is operator-controlled config, not user input stdin, err := cmd.StdinPipe() if err != nil { return nil, fmt.Errorf("decoder stdin: %w", err) @@ -539,9 +539,9 @@ func writeIVFHeader(w io.Writer, fourCC string, width, height, frameRate int) er binary.LittleEndian.PutUint16(header[4:6], 0) binary.LittleEndian.PutUint16(header[6:8], 32) copy(header[8:12], []byte(fourCC)) - binary.LittleEndian.PutUint16(header[12:14], uint16(width)) - binary.LittleEndian.PutUint16(header[14:16], uint16(height)) - binary.LittleEndian.PutUint32(header[16:20], uint32(frameRate)) + binary.LittleEndian.PutUint16(header[12:14], uint16(width)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic + binary.LittleEndian.PutUint16(header[14:16], uint16(height)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic + binary.LittleEndian.PutUint32(header[16:20], uint32(frameRate)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic binary.LittleEndian.PutUint32(header[20:24], 1) binary.LittleEndian.PutUint32(header[24:28], 0) binary.LittleEndian.PutUint32(header[28:32], 0) @@ -550,7 +550,7 @@ func writeIVFHeader(w io.Writer, fourCC string, width, height, frameRate int) er func writeIVFFrame(w io.Writer, pts uint64, frame []byte) error { header := make([]byte, 12) - binary.LittleEndian.PutUint32(header[0:4], uint32(len(frame))) + binary.LittleEndian.PutUint32(header[0:4], uint32(len(frame))) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic binary.LittleEndian.PutUint64(header[4:12], pts) if err := writeAll(w, header); err != nil { return err diff --git a/internal/transport/videochannel/frame.go b/internal/transport/videochannel/frame.go index 5a4f908..30233a8 100644 --- a/internal/transport/videochannel/frame.go +++ b/internal/transport/videochannel/frame.go @@ -71,9 +71,9 @@ func encodeDataFrame(seq, crc uint32, totalLen, fragIdx, fragTotal int, payload out[5] = frameTypeData binary.BigEndian.PutUint32(out[6:10], seq) binary.BigEndian.PutUint32(out[10:14], crc) - binary.BigEndian.PutUint32(out[14:18], uint32(totalLen)) - binary.BigEndian.PutUint16(out[18:20], uint16(fragIdx)) - binary.BigEndian.PutUint16(out[20:22], uint16(fragTotal)) + binary.BigEndian.PutUint32(out[14:18], uint32(totalLen)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic + binary.BigEndian.PutUint16(out[18:20], uint16(fragIdx)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic + binary.BigEndian.PutUint16(out[20:22], uint16(fragTotal)) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic copy(out[22:], payload) return out } diff --git a/internal/transport/videochannel/frame_extra_test.go b/internal/transport/videochannel/frame_extra_test.go index e55b960..075e1b1 100644 --- a/internal/transport/videochannel/frame_extra_test.go +++ b/internal/transport/videochannel/frame_extra_test.go @@ -11,6 +11,11 @@ import ( "github.com/pion/webrtc/v4" ) +var ( + errVideoFrameBase = errors.New("base") + errVideoFrameBoom = errors.New("boom") +) + func TestFragmentPayload(t *testing.T) { frags := fragmentPayload([]byte("abcdef"), 2) want := [][]byte{[]byte("ab"), []byte("cd"), []byte("ef")} @@ -56,6 +61,7 @@ func TestDecodeTransportFrameErrorsAndAck(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestCodecSpecsAndArgs(t *testing.T) { for _, mime := range []string{webrtc.MimeTypeH264, webrtc.MimeTypeVP8, webrtc.MimeTypeVP9} { spec, ok := codecSpecForMime(mime) @@ -79,7 +85,7 @@ func TestCodecSpecsAndArgs(t *testing.T) { if got := resolveEncoderCodec(vp9CodecSpec(), "nvenc"); got != "vp9_nvenc" { t.Fatalf("resolveEncoderCodec(vp9,nvenc) = %q", got) } - if got := resolveEncoderCodec(codecSpec{mimeType: webrtc.MimeTypeAV1, encoder: "libaom-av1"}, "nvenc"); got != "av1_nvenc" { + if got := resolveEncoderCodec(codecSpec{mimeType: webrtc.MimeTypeAV1, encoder: "libaom-av1"}, "nvenc"); got != "av1_nvenc" { //nolint:lll // long test description t.Fatalf("resolveEncoderCodec(av1,nvenc) = %q", got) } @@ -140,6 +146,7 @@ type bufferWriteCloser struct { func (w *bufferWriteCloser) Close() error { return nil } +//nolint:cyclop // table-driven test naturally has many branches func TestIVFWritersAndWithStderr(t *testing.T) { var buf bytes.Buffer if err := writeIVFHeader(&buf, "VP80", 320, 240, 30); err != nil { @@ -164,7 +171,7 @@ func TestIVFWritersAndWithStderr(t *testing.T) { t.Fatalf("writeAll(errWriter) error = %v", err) } - baseErr := errors.New("base") + baseErr := errVideoFrameBase if got := withStderr(baseErr, bytes.NewBufferString(" details \n")); got == nil || got.Error() != "base: details" { t.Fatalf("withStderr() = %v", got) } @@ -182,13 +189,13 @@ func TestFFmpegProcessErrAndFrameValidation(t *testing.T) { if _, err := enc.EncodeFrame([]byte("bad")); !errors.Is(err, ErrUnexpectedFrameSize) { t.Fatalf("EncodeFrame(short) error = %v, want %v", err, ErrUnexpectedFrameSize) } - enc.setErr(errors.New("boom")) + enc.setErr(errVideoFrameBoom) if _, err := enc.EncodeFrame([]byte("good")); err == nil || !strings.Contains(err.Error(), "encoder failed") { t.Fatalf("EncodeFrame(processErr) error = %v", err) } dec := &ffmpegDecoder{stderr: bytes.NewBufferString("decoder failed")} - dec.setErr(errors.New("boom")) + dec.setErr(errVideoFrameBoom) if err := dec.PushSample([]byte("sample")); err == nil || !strings.Contains(err.Error(), "decoder failed") { t.Fatalf("PushSample(processErr) error = %v", err) } @@ -199,6 +206,7 @@ func TestFFmpegProcessErrAndFrameValidation(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestFFmpegReadersAndSampleWriters(t *testing.T) { var ivf bytes.Buffer if err := writeIVFHeader(&ivf, "VP80", 2, 2, 30); err != nil { diff --git a/internal/transport/videochannel/inbound_test.go b/internal/transport/videochannel/inbound_test.go index 6a76c72..46f8a3e 100644 --- a/internal/transport/videochannel/inbound_test.go +++ b/internal/transport/videochannel/inbound_test.go @@ -21,7 +21,7 @@ func TestInboundAssemblyAndAck(t *testing.T) { typ: frameTypeData, seq: 1, crc: crc, - totalLen: uint32(len(payload)), + totalLen: uint32(len(payload)), //nolint:gosec // G115: bounded conversion verified by surrounding logic fragIdx: 1, fragTotal: 2, payload: []byte(" video"), @@ -34,7 +34,7 @@ func TestInboundAssemblyAndAck(t *testing.T) { typ: frameTypeData, seq: 1, crc: crc, - totalLen: uint32(len(payload)), + totalLen: uint32(len(payload)), //nolint:gosec // G115: bounded conversion verified by surrounding logic fragIdx: 0, fragTotal: 2, payload: []byte("hello"), diff --git a/internal/transport/videochannel/transport.go b/internal/transport/videochannel/transport.go index 5076c0d..6410d85 100644 --- a/internal/transport/videochannel/transport.go +++ b/internal/transport/videochannel/transport.go @@ -70,7 +70,7 @@ type streamTransport struct { videoCodec string videoTileModule int videoTileRS int - runCtx context.Context + runCtx context.Context //nolint:containedctx,lll // long-lived context drives idle-frame loops bound to this transport's lifetime idleFrame []byte idleFrameMu sync.Mutex @@ -571,7 +571,7 @@ func (p *streamTransport) assembleMessage(msg *inboundMessage) []byte { for _, frag := range msg.frags { data = append(data, frag...) } - if uint32(len(data)) > msg.totalLen { + if uint32(len(data)) > msg.totalLen { //nolint:gosec // G115: bounded conversion verified by surrounding logic data = data[:msg.totalLen] } return data diff --git a/internal/transport/videochannel/transport_unit_test.go b/internal/transport/videochannel/transport_unit_test.go index 3a257a3..3a9357e 100644 --- a/internal/transport/videochannel/transport_unit_test.go +++ b/internal/transport/videochannel/transport_unit_test.go @@ -12,6 +12,11 @@ import ( "github.com/pion/webrtc/v4" ) +var ( + errVideoUnitBoom = errors.New("boom") + errVideoUnitOpenBoom = errors.New("open boom") +) + type fakeVideoSession struct { stream *fakeVideoStream err error @@ -55,6 +60,7 @@ type nonVideoSession struct{} func (s *nonVideoSession) Capabilities() carrier.Capabilities { return carrier.Capabilities{} } +//nolint:cyclop // table-driven test naturally has many branches func TestNewCallbacksFeaturesAndClose(t *testing.T) { stream := &fakeVideoStream{canSend: true} name := "videochannel-unit-new" @@ -75,7 +81,10 @@ func TestNewCallbacksFeaturesAndClose(t *testing.T) { if err != nil { t.Fatalf("New() error = %v", err) } - tr := trIface.(*streamTransport) + tr, ok := trIface.(*streamTransport) + if !ok { + t.Fatalf("transport type = %T, want *streamTransport", trIface) + } if !stream.trackAdded || stream.trackCB == nil { t.Fatal("New() did not attach track and handler") } @@ -89,7 +98,7 @@ func TestNewCallbacksFeaturesAndClose(t *testing.T) { if !tr.CanSend() { t.Fatal("CanSend() = false, want true") } - if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { + if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { //nolint:lll // long test description t.Fatalf("Features() = %+v", features) } if tr.videoQRSize != defaultFragmentSize || tr.videoTileModule != 4 || tr.videoTileRS != 20 { @@ -102,23 +111,23 @@ func TestNewCallbacksFeaturesAndClose(t *testing.T) { func TestNewErrorPaths(t *testing.T) { carrier.Register("videochannel-create-fails", func(context.Context, carrier.Config) (carrier.Session, error) { - return nil, errors.New("boom") + return nil, errVideoUnitBoom }) - if _, err := New(context.Background(), transport.Config{Carrier: "videochannel-create-fails"}); err == nil || err.Error() != "create carrier transport: boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "videochannel-create-fails"}); err == nil || err.Error() != "create carrier transport: boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } carrier.Register("videochannel-no-video", func(context.Context, carrier.Config) (carrier.Session, error) { return &nonVideoSession{}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "videochannel-no-video"}); !errors.Is(err, ErrVideoTrackUnsupported) { + if _, err := New(context.Background(), transport.Config{Carrier: "videochannel-no-video"}); !errors.Is(err, ErrVideoTrackUnsupported) { //nolint:lll // long test description t.Fatalf("New() error = %v, want %v", err, ErrVideoTrackUnsupported) } carrier.Register("videochannel-open-fails", func(context.Context, carrier.Config) (carrier.Session, error) { - return &fakeVideoSession{err: errors.New("open boom")}, nil + return &fakeVideoSession{err: errVideoUnitOpenBoom}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "videochannel-open-fails"}); err == nil || err.Error() != "open video track: open boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "videochannel-open-fails"}); err == nil || err.Error() != "open video track: open boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } } @@ -160,6 +169,7 @@ func TestSendAckAndClosePaths(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestOutboundPriorityRenderAndClosedEnqueue(t *testing.T) { tr := &streamTransport{ stream: &fakeVideoStream{canSend: true}, diff --git a/internal/transport/videochannel/visual.go b/internal/transport/videochannel/visual.go index 8339d93..1fa784f 100644 --- a/internal/transport/videochannel/visual.go +++ b/internal/transport/videochannel/visual.go @@ -129,7 +129,7 @@ func extractTilePayload(frame []byte, tileModule, tileRS int) ([]byte, error) { result, err := c.Decode(frame) if err != nil { - return nil, nil + return nil, nil //nolint:nilerr // decode failures are treated as "no payload" by callers } return result.Payload, nil diff --git a/internal/transport/vp8channel/kcp.go b/internal/transport/vp8channel/kcp.go index d6c2208..1fa6592 100644 --- a/internal/transport/vp8channel/kcp.go +++ b/internal/transport/vp8channel/kcp.go @@ -134,7 +134,7 @@ func (r *kcpRuntime) send(msg []byte) error { return ErrKCPMessageTooLarge } var hdr [kcpLenPrefix]byte - binary.BigEndian.PutUint32(hdr[:], uint32(len(msg))) + binary.BigEndian.PutUint32(hdr[:], uint32(len(msg))) //nolint:gosec,lll // G115: bounded conversion verified by surrounding logic r.writeMu.Lock() defer r.writeMu.Unlock() diff --git a/internal/transport/vp8channel/kcpconn_test.go b/internal/transport/vp8channel/kcpconn_test.go index 00aed97..78fcf3b 100644 --- a/internal/transport/vp8channel/kcpconn_test.go +++ b/internal/transport/vp8channel/kcpconn_test.go @@ -8,6 +8,7 @@ import ( "time" ) +//nolint:cyclop // table-driven test naturally has many branches func TestKCPConnReadWriteDeadlinesAndClose(t *testing.T) { out := make(chan []byte, 1) hdr := testEpochHdr(9) diff --git a/internal/transport/vp8channel/transport.go b/internal/transport/vp8channel/transport.go index 0772f85..c1504ff 100644 --- a/internal/transport/vp8channel/transport.go +++ b/internal/transport/vp8channel/transport.go @@ -62,7 +62,7 @@ var ( ErrTransportClosed = errors.New("vp8channel transport closed") ) -var vp8Keepalive = []byte{ +var vp8Keepalive = []byte{ //nolint:gochecknoglobals // package-level state intentional 0x30, 0x01, 0x00, 0x9d, 0x01, 0x2a, 0x10, 0x00, 0x10, 0x00, 0x00, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0xfc, @@ -212,7 +212,7 @@ func randomEpoch() uint32 { if _, err := rand.Read(b[:]); err != nil { // rand.Read on Linux essentially never fails; fall back to a // time-derived value rather than panic. - return uint32(time.Now().UnixNano()) + return uint32(time.Now().UnixNano()) //nolint:gosec // G115: bounded conversion verified by surrounding logic } e := binary.BigEndian.Uint32(b[:]) if e == 0 { diff --git a/internal/transport/vp8channel/transport_unit_test.go b/internal/transport/vp8channel/transport_unit_test.go index f8a9083..bc506c5 100644 --- a/internal/transport/vp8channel/transport_unit_test.go +++ b/internal/transport/vp8channel/transport_unit_test.go @@ -14,6 +14,11 @@ import ( "github.com/pion/webrtc/v4" ) +var ( + errVP8UnitBoom = errors.New("boom") + errVP8UnitOpenBoom = errors.New("open boom") +) + type fakeVideoSession struct { stream *fakeVideoStream err error @@ -77,6 +82,7 @@ type nonVideoSession struct{} func (s *nonVideoSession) Capabilities() carrier.Capabilities { return carrier.Capabilities{} } +//nolint:cyclop // table-driven test naturally has many branches func TestNewConnectSendCallbacksFeaturesAndClose(t *testing.T) { stream := &fakeVideoStream{canSend: true} name := "vp8channel-unit-new" @@ -93,7 +99,10 @@ func TestNewConnectSendCallbacksFeaturesAndClose(t *testing.T) { if err != nil { t.Fatalf("New() error = %v", err) } - tr := trIface.(*streamTransport) + tr, ok := trIface.(*streamTransport) + if !ok { + t.Fatalf("transport type = %T, want *streamTransport", trIface) + } if !stream.trackAdded || stream.trackCB == nil { t.Fatal("New() did not attach track and handler") } @@ -125,7 +134,7 @@ func TestNewConnectSendCallbacksFeaturesAndClose(t *testing.T) { if !tr.CanSend() { t.Fatal("CanSend() = false, want true") } - if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { + if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { //nolint:lll // long test description t.Fatalf("Features() = %+v", features) } if err := tr.Send([]byte("payload")); err != nil { @@ -142,27 +151,28 @@ func TestNewConnectSendCallbacksFeaturesAndClose(t *testing.T) { func TestNewErrorPaths(t *testing.T) { carrier.Register("vp8channel-create-fails", func(context.Context, carrier.Config) (carrier.Session, error) { - return nil, errors.New("boom") + return nil, errVP8UnitBoom }) - if _, err := New(context.Background(), transport.Config{Carrier: "vp8channel-create-fails"}); err == nil || err.Error() != "create carrier transport: boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "vp8channel-create-fails"}); err == nil || err.Error() != "create carrier transport: boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } carrier.Register("vp8channel-no-video", func(context.Context, carrier.Config) (carrier.Session, error) { return &nonVideoSession{}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "vp8channel-no-video"}); !errors.Is(err, ErrVideoTrackUnsupported) { + if _, err := New(context.Background(), transport.Config{Carrier: "vp8channel-no-video"}); !errors.Is(err, ErrVideoTrackUnsupported) { //nolint:lll // long test description t.Fatalf("New() error = %v, want %v", err, ErrVideoTrackUnsupported) } carrier.Register("vp8channel-open-fails", func(context.Context, carrier.Config) (carrier.Session, error) { - return &fakeVideoSession{err: errors.New("open boom")}, nil + return &fakeVideoSession{err: errVP8UnitOpenBoom}, nil }) - if _, err := New(context.Background(), transport.Config{Carrier: "vp8channel-open-fails"}); err == nil || err.Error() != "open video track: open boom" { + if _, err := New(context.Background(), transport.Config{Carrier: "vp8channel-open-fails"}); err == nil || err.Error() != "open video track: open boom" { //nolint:lll // long test description t.Fatalf("New() error = %v", err) } } +//nolint:cyclop // table-driven test naturally has many branches func TestEpochHeaderTokenAndOutboundCapacity(t *testing.T) { tr := &streamTransport{ stream: &fakeVideoStream{canSend: true}, @@ -256,6 +266,7 @@ func TestVP8FrameStateAssemblesAndRejectsCorruptFrames(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestHandleIncomingFrameEpochFilteringAndReconnect(t *testing.T) { called := 0 tr := &streamTransport{ @@ -293,7 +304,10 @@ func TestHandleIncomingFrameEpochFilteringAndReconnect(t *testing.T) { reconnected := false tr.SetReconnectCallback(func() { reconnected = true }) - stream := tr.stream.(*fakeVideoStream) + stream, ok := tr.stream.(*fakeVideoStream) + if !ok { + t.Fatalf("stream type = %T, want *fakeVideoStream", tr.stream) + } if stream.reconnect == nil { t.Fatal("SetReconnectCallback did not install stream callback") } @@ -304,6 +318,6 @@ func TestHandleIncomingFrameEpochFilteringAndReconnect(t *testing.T) { reconnected = false tr.handleIncomingFrame(mkFrame(tr.bindingToken, 2, []byte("after-restart"))) if !reconnected || tr.peerEpoch.Load() != 2 || tr.kcp == nil { - t.Fatalf("epoch change did not reset/reconnect: reconnected=%v epoch=%d kcp=%v", reconnected, tr.peerEpoch.Load(), tr.kcp) + t.Fatalf("epoch change did not reset/reconnect: reconnected=%v epoch=%d kcp=%v", reconnected, tr.peerEpoch.Load(), tr.kcp) //nolint:lll // long test description } } diff --git a/mobile/mobile.go b/mobile/mobile.go index 9a361e0..ffb7a9c 100644 --- a/mobile/mobile.go +++ b/mobile/mobile.go @@ -65,14 +65,14 @@ const ( ) var ( - mu sync.Mutex - defaults mobileConfig - defaultsSet sync.Once - registerSet sync.Once - runClientWithReady = client.RunWithReady - cancel context.CancelFunc - done chan struct{} - ready chan struct{} + mu sync.Mutex //nolint:gochecknoglobals // package-level state intentional + defaults mobileConfig //nolint:gochecknoglobals // package-level state intentional + defaultsSet sync.Once //nolint:gochecknoglobals // package-level state intentional + registerSet sync.Once //nolint:gochecknoglobals // package-level state intentional + runClientWithReady = client.RunWithReady //nolint:gochecknoglobals // package-level state intentional + cancel context.CancelFunc //nolint:gochecknoglobals // package-level state intentional + done chan struct{} //nolint:gochecknoglobals // package-level state intentional + ready chan struct{} //nolint:gochecknoglobals // package-level state intentional errRun error ) @@ -616,6 +616,7 @@ func startWithConfig( } // WaitReady blocks until the selected transport is connected and the local SOCKS5 listener is ready. +//nolint:cyclop // straightforward state-machine waits with multiple terminal conditions func WaitReady(timeoutMillis int) error { mu.Lock() r := ready diff --git a/mobile/mobile_test.go b/mobile/mobile_test.go index b507a3c..1db8990 100644 --- a/mobile/mobile_test.go +++ b/mobile/mobile_test.go @@ -48,7 +48,12 @@ func resetMobileGlobals(t *testing.T) { logger.SetVerbose(false) } -var clientRunWithReady = runClientWithReady +var clientRunWithReady = runClientWithReady //nolint:gochecknoglobals // package-level state intentional + +var ( + errMobileCheckFailed = errors.New("check failed") + errMobileRunFailed = errors.New("run failed") +) func TestProtectorAndLogging(t *testing.T) { resetMobileGlobals(t) @@ -96,6 +101,7 @@ func TestDefaultsAndSetters(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestNormalizeBuildRoomAndClamp(t *testing.T) { tests := map[string]string{ "datachannel": dataTransport, @@ -133,28 +139,29 @@ func TestNormalizeBuildRoomAndClamp(t *testing.T) { func TestStartValidation(t *testing.T) { resetMobileGlobals(t) - if err := startWithConfig("", dataTransport, "room", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errCarrierRequired) { + if err := startWithConfig("", dataTransport, "room", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errCarrierRequired) { //nolint:lll // long test description t.Fatalf("startWithConfig(missing carrier) = %v", err) } - if err := startWithConfig("telemost", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errRoomIDRequired) { + if err := startWithConfig("telemost", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errRoomIDRequired) { //nolint:lll // long test description t.Fatalf("startWithConfig(missing room) = %v", err) } - if err := startWithConfig("jazz", dataTransport, "", "", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errClientIDRequired) { + if err := startWithConfig("jazz", dataTransport, "", "", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errClientIDRequired) { //nolint:lll // long test description t.Fatalf("startWithConfig(missing client) = %v", err) } - if err := startWithConfig("jazz", dataTransport, "", "client", "", 1080, "", "", mobileConfig{}); !errors.Is(err, errKeyHexRequired) { + if err := startWithConfig("jazz", dataTransport, "", "client", "", 1080, "", "", mobileConfig{}); !errors.Is(err, errKeyHexRequired) { //nolint:lll // long test description t.Fatalf("startWithConfig(missing key) = %v", err) } mu.Lock() cancel = func() {} mu.Unlock() - if err := startWithConfig("jazz", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errAlreadyRunning) { + if err := startWithConfig("jazz", dataTransport, "", "client", "key", 1080, "", "", mobileConfig{}); !errors.Is(err, errAlreadyRunning) { //nolint:lll // long test description t.Fatalf("startWithConfig(running) = %v", err) } resetMobileGlobals(t) } +//nolint:cyclop // table-driven test naturally has many branches func TestStartWithInjectedRunnerLifecycle(t *testing.T) { resetMobileGlobals(t) t.Cleanup(func() { @@ -163,26 +170,26 @@ func TestStartWithInjectedRunnerLifecycle(t *testing.T) { runClientWithReady = func( ctx context.Context, - linkName, transportName, carrierName, roomURL, keyHex, clientID string, + linkName, transportName, carrierName, roomURL, _, clientID string, localAddr string, - dnsServer, socksUser, socksPass string, + dnsServer, _, _ string, onReady func(), - videoWidth int, - videoHeight int, - videoFPS int, - videoBitrate string, - videoHW string, - videoQRSize int, - videoQRRecovery string, - videoCodec string, - videoTileModule int, - videoTileRS int, + _ int, + _ int, + _ int, + _ string, + _ string, + _ int, + _ string, + _ string, + _ int, + _ int, vp8FPS int, vp8BatchSize int, - seiFPS int, - seiBatchSize int, - seiFragmentSize int, - seiAckTimeoutMS int, + _ int, + _ int, + _ int, + _ int, ) error { if linkName != defaultLink || transportName != dataTransport || carrierName != carrierJazz || roomURL != "any" || clientID != "client" || localAddr != "127.0.0.1:1080" || @@ -210,6 +217,7 @@ func TestStartWithInjectedRunnerLifecycle(t *testing.T) { } } +//nolint:cyclop // table-driven test naturally has many branches func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) { resetMobileGlobals(t) t.Cleanup(func() { @@ -218,26 +226,26 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) { runClientWithReady = func( ctx context.Context, - linkName, transportName, carrierName, roomURL, keyHex, clientID string, + _, transportName, _, roomURL, _, _ string, localAddr string, - dnsServer, socksUser, socksPass string, + _, socksUser, socksPass string, onReady func(), - videoWidth int, - videoHeight int, - videoFPS int, - videoBitrate string, - videoHW string, - videoQRSize int, - videoQRRecovery string, - videoCodec string, - videoTileModule int, - videoTileRS int, - vp8FPS int, - vp8BatchSize int, - seiFPS int, - seiBatchSize int, - seiFragmentSize int, - seiAckTimeoutMS int, + _ int, + _ int, + _ int, + _ string, + _ string, + _ int, + _ string, + _ string, + _ int, + _ int, + _ int, + _ int, + _ int, + _ int, + _ int, + _ int, ) error { if transportName != defaultTransport || roomURL != "https://telemost.yandex.ru/j/room" || localAddr != "127.0.0.1:1081" || socksUser != "u" || socksPass != "p" { @@ -259,26 +267,26 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) { runClientWithReady = func( ctx context.Context, - linkName, transportName, carrierName, roomURL, keyHex, clientID string, - localAddr string, - dnsServer, socksUser, socksPass string, + _, transportName, _, _, _, _ string, + _ string, + _, _, _ string, onReady func(), - videoWidth int, - videoHeight int, - videoFPS int, - videoBitrate string, - videoHW string, - videoQRSize int, - videoQRRecovery string, - videoCodec string, - videoTileModule int, - videoTileRS int, + _ int, + _ int, + _ int, + _ string, + _ string, + _ int, + _ string, + _ string, + _ int, + _ int, vp8FPS int, vp8BatchSize int, - seiFPS int, - seiBatchSize int, - seiFragmentSize int, - seiAckTimeoutMS int, + _ int, + _ int, + _ int, + _ int, ) error { if transportName != dataTransport || vp8FPS != 1 || vp8BatchSize != 64 { t.Fatalf("Check args mismatch: transport=%q vp8=%d/%d", transportName, vp8FPS, vp8BatchSize) @@ -304,35 +312,35 @@ func TestCheckTimeoutAndRunError(t *testing.T) { runClientWithReady = func( ctx context.Context, - linkName, transportName, carrierName, roomURL, keyHex, clientID string, - localAddr string, - dnsServer, socksUser, socksPass string, - onReady func(), - videoWidth int, - videoHeight int, - videoFPS int, - videoBitrate string, - videoHW string, - videoQRSize int, - videoQRRecovery string, - videoCodec string, - videoTileModule int, - videoTileRS int, - vp8FPS int, - vp8BatchSize int, - seiFPS int, - seiBatchSize int, - seiFragmentSize int, - seiAckTimeoutMS int, + _, _, _, _, _, _ string, + _ string, + _, _, _ string, + _ func(), + _ int, + _ int, + _ int, + _ string, + _ string, + _ int, + _ string, + _ string, + _ int, + _ int, + _ int, + _ int, + _ int, + _ int, + _ int, + _ int, ) error { <-ctx.Done() return nil } - if _, err := Check("telemost", defaultTransport, "room", "client", "key", 1083, 1, 30, 1); !errors.Is(err, errStartTimedOut) { + if _, err := Check("telemost", defaultTransport, "room", "client", "key", 1083, 1, 30, 1); !errors.Is(err, errStartTimedOut) { //nolint:lll // long test description t.Fatalf("Check(timeout) error = %v, want %v", err, errStartTimedOut) } - want := errors.New("check failed") + want := errMobileCheckFailed runClientWithReady = func( context.Context, string, string, string, string, string, string, @@ -369,7 +377,7 @@ func TestWaitReadyStatesAndStop(t *testing.T) { } mu.Lock() - errRun = errors.New("run failed") + errRun = errMobileRunFailed mu.Unlock() if err := WaitReady(1); err == nil || err.Error() != "run failed" { t.Fatalf("WaitReady(run err) = %v", err)