5
5
"context"
6
6
"encoding/json"
7
7
"fmt"
8
+ "io"
8
9
"net/url"
9
10
"os"
10
11
"os/exec"
@@ -93,30 +94,34 @@ type TestRunnerWithFileLogger interface {
93
94
94
95
// runner struct implements TestRunner interface
95
96
type runner struct {
96
- registry * registry.Registry
97
- validators []types.ValidatorMetadata
98
- workDir string // Directory for running tests
99
- log log.Logger
100
- runID string
101
- goBinary string // Path to the Go binary
102
- allowSkips bool // Whether to allow skipping tests when preconditions are not met
103
- fileLogger * logging.FileLogger // Logger for storing test results
104
- networkName string // Name of the network being tested
105
- env * env.DevnetEnv
106
- tracer trace.Tracer
97
+ registry * registry.Registry
98
+ validators []types.ValidatorMetadata
99
+ workDir string // Directory for running tests
100
+ log log.Logger
101
+ runID string
102
+ goBinary string // Path to the Go binary
103
+ allowSkips bool // Whether to allow skipping tests when preconditions are not met
104
+ outputTestLogs bool // Whether to output test logs to the console
105
+ testLogLevel string // Log level to be used for the tests
106
+ fileLogger * logging.FileLogger // Logger for storing test results
107
+ networkName string // Name of the network being tested
108
+ env * env.DevnetEnv
109
+ tracer trace.Tracer
107
110
}
108
111
109
112
// Config holds configuration for creating a new runner
110
113
type Config struct {
111
- Registry * registry.Registry
112
- TargetGate string
113
- WorkDir string
114
- Log log.Logger
115
- GoBinary string // path to the Go binary
116
- AllowSkips bool // Whether to allow skipping tests when preconditions are not met
117
- FileLogger * logging.FileLogger // Logger for storing test results
118
- NetworkName string // Name of the network being tested
119
- DevnetEnv * env.DevnetEnv
114
+ Registry * registry.Registry
115
+ TargetGate string
116
+ WorkDir string
117
+ Log log.Logger
118
+ GoBinary string // path to the Go binary
119
+ AllowSkips bool // Whether to allow skipping tests when preconditions are not met
120
+ OutputTestLogs bool // Whether to output test logs to the console
121
+ TestLogLevel string // Log level to be used for the tests
122
+ FileLogger * logging.FileLogger // Logger for storing test results
123
+ NetworkName string // Name of the network being tested
124
+ DevnetEnv * env.DevnetEnv
120
125
}
121
126
122
127
// NewTestRunner creates a new test runner instance
@@ -352,27 +357,10 @@ func (r *runner) getTestKey(validator types.ValidatorMetadata) string {
352
357
return validator .FuncName
353
358
}
354
359
355
- func (r * runner ) isGitInstalled () error {
356
- gitCmd := exec .Command ("git" , "version" )
357
- err := gitCmd .Run ()
358
- if err != nil {
359
- return fmt .Errorf ("git is not installed" )
360
- }
361
- return nil
362
- }
363
-
364
360
func isLocalPath (pkg string ) bool {
365
361
return strings .HasPrefix (pkg , "./" ) || strings .HasPrefix (pkg , "/" ) || strings .HasPrefix (pkg , "../" )
366
362
}
367
363
368
- func isGitRemotePath (pkg string ) bool {
369
- return strings .HasPrefix (pkg , "git::" ) ||
370
- strings .HasPrefix (pkg , "git@" ) ||
371
- strings .Contains (pkg , "github.com/" ) ||
372
- strings .Contains (pkg , "bitbucket.org/" ) ||
373
- strings .Contains (pkg , "golang.org/" )
374
- }
375
-
376
364
// RunTest implements the TestRunner interface
377
365
func (r * runner ) RunTest (ctx context.Context , metadata types.ValidatorMetadata ) (* types.TestResult , error ) {
378
366
// Use defer and recover to catch panics and convert them to errors
@@ -401,7 +389,7 @@ func (r *runner) RunTest(ctx context.Context, metadata types.ValidatorMetadata)
401
389
}
402
390
}()
403
391
404
- // Check if the path is available locally, otherwise check if git is installed
392
+ // Check if the path is available locally
405
393
if isLocalPath (metadata .Package ) {
406
394
fullPath := filepath .Join (r .workDir , metadata .Package )
407
395
if _ , statErr := os .Stat (fullPath ); os .IsNotExist (statErr ) {
@@ -412,15 +400,6 @@ func (r *runner) RunTest(ctx context.Context, metadata types.ValidatorMetadata)
412
400
Error : fmt .Errorf ("local package path does not exist: %s" , fullPath ),
413
401
}, nil
414
402
}
415
- } else if isGitRemotePath (metadata .Package ) {
416
- if err := r .isGitInstalled (); err != nil {
417
- r .log .Error ("Git is not installed but required for remote package, failing test" , "validator" , metadata .ID , "package" , metadata .Package )
418
- return & types.TestResult {
419
- Metadata : metadata ,
420
- Status : types .TestStatusFail ,
421
- Error : fmt .Errorf ("git is not installed" ),
422
- }, nil
423
- }
424
403
}
425
404
426
405
r .log .Info ("Running validator" , "validator" , metadata .ID )
@@ -562,8 +541,16 @@ func (r *runner) runSingleTest(ctx context.Context, metadata types.ValidatorMeta
562
541
defer cleanup ()
563
542
564
543
var stdout , stderr bytes.Buffer
565
- cmd .Stdout = & stdout
566
- cmd .Stderr = & stderr
544
+ if r .outputTestLogs {
545
+ stdoutLogger := & logWriter {logFn : func (msg string ) { r .log .Info ("Test output" , "test" , metadata .FuncName , "output" , msg ) }}
546
+ stderrLogger := & logWriter {logFn : func (msg string ) { r .log .Error ("Test error output" , "test" , metadata .FuncName , "error" , msg ) }}
547
+
548
+ cmd .Stdout = io .MultiWriter (& stdout , stdoutLogger )
549
+ cmd .Stderr = io .MultiWriter (& stderr , stderrLogger )
550
+ } else {
551
+ cmd .Stdout = & stdout
552
+ cmd .Stderr = & stderr
553
+ }
567
554
568
555
r .log .Info ("Running test" , "test" , metadata .FuncName )
569
556
r .log .Debug ("Running test command" ,
@@ -609,6 +596,7 @@ func (r *runner) runSingleTest(ctx context.Context, metadata types.ValidatorMeta
609
596
610
597
// If we couldn't parse the output for some reason, create a minimal failing result
611
598
if parsedResult == nil {
599
+ r .log .Error ("test exited with non-zero exit code" , "exitCode" , cmd .ProcessState .ExitCode ())
612
600
parsedResult = & types.TestResult {
613
601
Metadata : metadata ,
614
602
Status : types .TestStatusFail ,
@@ -1210,16 +1198,16 @@ func (r *runner) testCommandContext(ctx context.Context, name string, arg ...str
1210
1198
r .log .Error ("Failed to write env to temp file" , "error" , err )
1211
1199
}
1212
1200
1213
- env := append (
1214
- os .Environ (),
1201
+ logLevelStr := r .testLogLevel
1202
+ runEnv := append ([]string {fmt .Sprintf ("LOG_LEVEL=%s" , logLevelStr )}, os .Environ ()... )
1203
+ runEnv = append (runEnv ,
1215
1204
// override the env URL with the one from the temp file
1216
1205
fmt .Sprintf ("%s=%s" , env .EnvURLVar , envFile .Name ()),
1217
1206
// override the control resolution scheme with the original one
1218
1207
fmt .Sprintf ("%s=%s" , env .EnvCtrlVar , url .Scheme ),
1219
- // Set DEVNET_EXPECT_PRECONDITIONS_MET=true to make tests fail instead of skip when preconditions are not met
1220
1208
"DEVNET_EXPECT_PRECONDITIONS_MET=true" ,
1221
1209
)
1222
- cmd .Env = telemetry .InstrumentEnvironment (ctx , env )
1210
+ cmd .Env = telemetry .InstrumentEnvironment (ctx , runEnv )
1223
1211
}
1224
1212
cleanup := func () {
1225
1213
if envFile != nil {
@@ -1233,3 +1221,22 @@ func (r *runner) testCommandContext(ctx context.Context, name string, arg ...str
1233
1221
// Make sure the runner type implements both interfaces
1234
1222
var _ TestRunner = & runner {}
1235
1223
var _ TestRunnerWithFileLogger = & runner {}
1224
+
1225
+ type logWriter struct {
1226
+ logFn func (msg string )
1227
+ buf []byte
1228
+ }
1229
+
1230
+ func (w * logWriter ) Write (p []byte ) (n int , err error ) {
1231
+ w .buf = append (w .buf , p ... )
1232
+ for {
1233
+ idx := bytes .IndexByte (w .buf , '\n' )
1234
+ if idx == - 1 {
1235
+ break
1236
+ }
1237
+ line := w .buf [:idx ]
1238
+ w .logFn (string (line ))
1239
+ w .buf = w .buf [idx + 1 :]
1240
+ }
1241
+ return len (p ), nil
1242
+ }
0 commit comments