-
Notifications
You must be signed in to change notification settings - Fork 109
/
robot_utils.go
125 lines (114 loc) · 3.93 KB
/
robot_utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Package robottestutils provides helper functions in testing
package robottestutils
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
"regexp"
"testing"
"time"
"go.uber.org/zap/zaptest/observer"
"go.viam.com/test"
"go.viam.com/utils/testutils"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"go.viam.com/rdk/config"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/robot/client"
weboptions "go.viam.com/rdk/robot/web/options"
)
// CreateBaseOptionsAndListener creates a new web options with random port as listener.
func CreateBaseOptionsAndListener(tb testing.TB) (weboptions.Options, net.Listener, string) {
tb.Helper()
var listener net.Listener = testutils.ReserveRandomListener(tb)
options := weboptions.New()
options.Network.BindAddress = ""
options.Network.Listener = listener
addr := listener.Addr().String()
return options, listener, addr
}
// NewRobotClient creates a new robot client with a certain address.
func NewRobotClient(tb testing.TB, logger logging.Logger, addr string, dur time.Duration) *client.RobotClient {
tb.Helper()
// start robot client
robotClient, err := client.New(
context.Background(),
addr,
logger,
client.WithRefreshEvery(dur),
client.WithCheckConnectedEvery(5*dur),
client.WithReconnectEvery(dur),
)
test.That(tb, err, test.ShouldBeNil)
return robotClient
}
// Connect creates a new grpc.ClientConn server running on localhost:port.
func Connect(port int) (*grpc.ClientConn, error) {
ctxTimeout, cancelFunc := context.WithTimeout(context.Background(), time.Minute)
defer cancelFunc()
var conn *grpc.ClientConn
conn, err := grpc.DialContext(ctxTimeout,
fmt.Sprintf("dns:///localhost:%d", port),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
return nil, err
}
return conn, nil
}
// MakeTempConfig writes a config.Config object to a temporary file for testing.
func MakeTempConfig(t *testing.T, cfg *config.Config, logger logging.Logger) (string, error) {
if err := cfg.Ensure(false, logger); err != nil {
return "", err
}
output, err := json.Marshal(cfg)
if err != nil {
return "", err
}
file, err := os.CreateTemp(t.TempDir(), "fake-*")
if err != nil {
return "", err
}
_, err = file.Write(output)
if err != nil {
return "", err
}
return file.Name(), file.Close()
}
// WaitForServing will scan the logs in the `observer` input until seeing a "serving" or "error
// serving web" message. For added accuracy, it also checks that the port a test is expecting to
// start a server on matches the one in the log message.
//
// WaitForServing will return true if the server has started successfully in the allotted time, and
// false otherwise.
//nolint
func WaitForServing(observer *observer.ObservedLogs, port int) bool {
// Message:"\n\\_ 2024-02-07T20:47:03.576Z\tINFO\trobot_server\tweb/web.go:598\tserving\t{\"url\":\"http://127.0.0.1:20000\"}"
successRegex := regexp.MustCompile(fmt.Sprintf("\tserving\t.*:%d\"", port))
// Message:"\n\\_ 2024-02-02T14:43:02.862Z\tERROR\trobot_server\tserver/entrypoint.go:177\terror serving web\t{\"error\":\"listen tcp 127.0.0.1:8090: bind: address already in use\"}"
failRegex := regexp.MustCompile(fmt.Sprintf("\terror serving web\t.*:%d:", port))
lastSeenLogIdx := 0
for tryNum := 0; tryNum < 60; tryNum++ {
// `ObservedLogs.All` does not "consume" the logs it is holding internally (whereas
// `ObservedLogs.TakeAll` does). Some tests assert on logs that happen prior to serving on
// an address. We could scan all the logs on each pass. But instead we introduce the
// optimization of only scanning logs that were new since the last scan with
// `lastSeenLogIdx`.
newLogs := observer.All()[lastSeenLogIdx:]
lastSeenLogIdx += len(newLogs)
for _, log := range newLogs {
switch {
case successRegex.MatchString(log.Message):
return true
case failRegex.MatchString(log.Message):
return false
default:
}
}
time.Sleep(time.Second)
}
return false
}