blob: 04466c44d283781ab2c30de2aaa6af6d723ec443 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sampler_test
import (
"strings"
"testing"
"time"
"v.io/v23/context"
tu "v.io/x/ref/test/testutil"
"v.io/x/sensorlog_lite/internal/measure/sampler"
"v.io/x/sensorlog_lite/internal/sbmodel"
)
// Runs the sampler script and verifies that the callback was called with the
// expected value or error.
func runSamplerTest(t *testing.T, samDef *sbmodel.SamplerDef) sbmodel.VDataPoint {
ctx, cancel := context.RootContext()
defer cancel()
var gotRes *sampler.MeasureResult
start := time.Now()
if err := sampler.Run(ctx, samDef, func(res *sampler.MeasureResult) error {
gotRes = res
return nil
}); err != nil {
t.Fatal(tu.FormatLogLine(3, "unexpected sampling error: %v", err))
}
end := time.Now()
if gotRes == nil {
t.Fatal(tu.FormatLogLine(3, "sampler callback was not called"))
}
if gotRes.Time.Before(start) || gotRes.Time.After(end) {
t.Error(tu.FormatLogLine(3, "invalid time: %v, expected between %v and %v", gotRes.Time, start, end))
}
return gotRes.Data
}
func runSamplerTestForValue(t *testing.T, samDef *sbmodel.SamplerDef, expectVal float64) {
res := runSamplerTest(t, samDef)
switch res := res.(type) {
case sbmodel.VDataPointValue:
if res.Value != expectVal {
t.Error(tu.FormatLogLine(2, "unexpected value, got: %v, want: %v", res.Value, expectVal))
}
default:
t.Error(tu.FormatLogLine(2, "unexpected type, got: %v, want value: %v", res, expectVal))
}
}
func runSamplerTestForError(t *testing.T, samDef *sbmodel.SamplerDef, expectErrPrefix string) {
res := runSamplerTest(t, samDef)
switch res := res.(type) {
case sbmodel.VDataPointError:
if !strings.HasPrefix(res.Value, expectErrPrefix) {
t.Error(tu.FormatLogLine(2, "unexpected error, got: %v, want prefix: %v", res.Value, expectErrPrefix))
}
default:
t.Error(tu.FormatLogLine(2, "unexpected type, got: %v, want error with prefix: %v", res, expectErrPrefix))
}
}
func TestSamplerRun(t *testing.T) {
runSamplerTestForValue(t, &sbmodel.SamplerDef{
Script: `echo 42`,
Start: time.Now(),
Interval: time.Second, // 7.5M years overflows time.Duration
}, 42.0)
runSamplerTestForValue(t, &sbmodel.SamplerDef{
Script: `
for i in $(seq 10000); do :; done
printf "%f\n" -12.73
exit 0
`,
Start: time.Now(),
Interval: time.Millisecond,
}, -12.73)
runSamplerTestForError(t, &sbmodel.SamplerDef{
Script: `echo 42; exit 1`,
Start: time.Now(),
Interval: time.Second,
}, "Script error")
runSamplerTestForError(t, &sbmodel.SamplerDef{
Script: `exit 0`,
Start: time.Now(),
Interval: time.Second,
}, "Parse error")
runSamplerTestForError(t, &sbmodel.SamplerDef{
Script: `echo 42foo`,
Start: time.Now(),
Interval: time.Second,
}, "Parse error")
runSamplerTestForError(t, &sbmodel.SamplerDef{
Script: `echo 42; echo 47`,
Start: time.Now(),
Interval: time.Second,
}, "Parse error")
}
func TestSamplerCancel(t *testing.T) {
ctx, cancel := context.RootContext()
defer cancel()
// The script sleeps for 10 seconds, not interruptible by SIGINT/SIGTERM.
samDef := &sbmodel.SamplerDef{
Script: `
trap 'sleep 10' INT TERM
sleep 10
echo 42
`,
Start: time.Now(),
Interval: time.Second,
}
// Asynchronously run sampler, cancelling shortly after.
done := make(chan error, 1)
go func() {
done <- sampler.Run(ctx, samDef, func(res *sampler.MeasureResult) error {
t.Errorf("worker was cancelled, result callback should not have been called, got: %v", res.Data)
return nil
})
}()
time.Sleep(100 * time.Millisecond)
cancelTime := time.Now()
cancel()
if err := <-done; err != nil {
t.Errorf("unexpected sampling error: %v", err)
}
// Cancel should have killed the script using SIGKILL a short time after the
// script failed to exit on SIGINT.
if cancelTime.Add(1 * time.Second).Before(time.Now()) {
t.Errorf("sampling script took more than a second to stop")
}
}