203 lines
5.8 KiB
Go
203 lines
5.8 KiB
Go
// Copyright 2024 The Go 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 telemetry_test
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/telemetry"
|
|
"golang.org/x/telemetry/counter"
|
|
"golang.org/x/telemetry/counter/countertest"
|
|
"golang.org/x/telemetry/internal/configtest"
|
|
ic "golang.org/x/telemetry/internal/counter"
|
|
"golang.org/x/telemetry/internal/regtest"
|
|
it "golang.org/x/telemetry/internal/telemetry"
|
|
"golang.org/x/telemetry/internal/testenv"
|
|
)
|
|
|
|
// These environment variables are used to coordinate the fork+exec subprocess
|
|
// started by TestStart.
|
|
const (
|
|
runStartEnv = "X_TELEMETRY_TEST_START"
|
|
telemetryDirEnv = "X_TELEMETRY_TEST_START_TELEMETRY_DIR"
|
|
uploadURLEnv = "X_TELEMETRY_TEST_START_UPLOAD_URL"
|
|
asofEnv = "X_TELEMETRY_TEST_START_ASOF"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
// TestStart can't use internal/regtest, because Start itself also uses
|
|
// fork+exec to start a subprocess, which does not interact well with the
|
|
// fork+exec trick used by regtest.RunProg.
|
|
if prog := os.Getenv(runStartEnv); prog != "" {
|
|
os.Exit(runProg(prog))
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
// runProg runs the given program.
|
|
// See the switch statement below.
|
|
func runProg(prog string) int {
|
|
|
|
mustGetEnv := func(envvar string) string {
|
|
v := os.Getenv(envvar)
|
|
if v == "" {
|
|
log.Fatalf("missing required environment var %q", envvar)
|
|
}
|
|
return v
|
|
}
|
|
|
|
// Get the fake time used by all programs.
|
|
asof, err := time.Parse(it.DateOnly, mustGetEnv(asofEnv))
|
|
if err != nil {
|
|
log.Fatalf("parsing %s: %v", asofEnv, err)
|
|
}
|
|
|
|
// Set global state.
|
|
ic.CounterTime = func() time.Time { return asof } // must be done before Open
|
|
countertest.Open(mustGetEnv(telemetryDirEnv))
|
|
|
|
switch prog {
|
|
case "setmode":
|
|
// Use the modified time above for the asof time.
|
|
if err := it.Default.SetModeAsOf("on", asof); err != nil {
|
|
log.Fatalf("setting mode: %v", err)
|
|
}
|
|
case "inc":
|
|
// (CounterTime is already set above)
|
|
counter.Inc("teststart/counter")
|
|
|
|
case "start":
|
|
res := telemetry.Start(telemetry.Config{
|
|
// No need to set TelemetryDir since the Default dir is already set by countertest.Open.
|
|
Upload: true,
|
|
UploadURL: mustGetEnv(uploadURLEnv),
|
|
UploadStartTime: asof,
|
|
})
|
|
res.Wait()
|
|
default:
|
|
log.Fatalf("unknown program %q", prog)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func execProg(t *testing.T, telemetryDir, prog string, asof time.Time, env ...string) {
|
|
// Run the runStart function below, via a fork+exec trick.
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
cmd := exec.Command(exe, "** TestStart **") // this unused arg is just for ps(1)
|
|
cmd.Stderr = os.Stderr
|
|
cmd.Env = os.Environ()
|
|
cmd.Env = append(cmd.Env, asofEnv+"="+asof.Format(it.DateOnly))
|
|
cmd.Env = append(cmd.Env, telemetryDirEnv+"="+telemetryDir)
|
|
cmd.Env = append(cmd.Env, runStartEnv+"="+prog) // see TestMain
|
|
cmd.Env = append(cmd.Env, env...)
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
t.Errorf("program failed unexpectedly (%v)\n%s", err, out)
|
|
}
|
|
}
|
|
|
|
func TestStart(t *testing.T) {
|
|
testenv.SkipIfUnsupportedPlatform(t)
|
|
testenv.MustHaveExec(t)
|
|
|
|
// This test sets up a telemetry environment, and then runs a test program
|
|
// that increments a counter, and uploads via telemetry.Start.
|
|
|
|
telemetryDir := t.TempDir()
|
|
|
|
uploaded := false
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
uploaded = true
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Errorf("error reading body: %v", err)
|
|
} else {
|
|
if body := string(body); !strings.Contains(body, "teststart/counter") {
|
|
t.Errorf("upload does not contain \"teststart/counter\":\n%s", body)
|
|
}
|
|
}
|
|
}))
|
|
uploadEnv := []string{uploadURLEnv + "=" + server.URL}
|
|
|
|
uc := regtest.CreateTestUploadConfig(t, []string{"teststart/counter"}, nil)
|
|
uploadEnv = append(uploadEnv, configtest.LocalProxyEnv(t, uc, "v1.2.3")...)
|
|
|
|
// Script programs.
|
|
now := time.Now()
|
|
execProg(t, telemetryDir, "setmode", now.Add(-30*24*time.Hour)) // back-date telemetry acceptance
|
|
execProg(t, telemetryDir, "inc", now.Add(-8*24*time.Hour)) // increment the counter
|
|
execProg(t, telemetryDir, "start", now, uploadEnv...) // run start
|
|
|
|
if !uploaded {
|
|
t.Fatalf("no upload occurred on %v", os.Getpid())
|
|
}
|
|
}
|
|
|
|
func TestConcurrentStart(t *testing.T) {
|
|
testenv.SkipIfUnsupportedPlatform(t)
|
|
testenv.MustHaveExec(t)
|
|
|
|
telemetryDir := t.TempDir()
|
|
|
|
var uploadMu sync.Mutex
|
|
uploads := map[string]int{} // date -> uploads
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
key := r.URL.Path
|
|
if idx := strings.LastIndex(r.URL.Path, "/"); idx >= 0 {
|
|
key = r.URL.Path[idx+len("/"):]
|
|
}
|
|
uploadMu.Lock()
|
|
uploads[key]++
|
|
uploadMu.Unlock()
|
|
}))
|
|
uploadEnv := []string{uploadURLEnv + "=" + server.URL}
|
|
|
|
uc := regtest.CreateTestUploadConfig(t, []string{"teststart/counter"}, nil)
|
|
uploadEnv = append(uploadEnv, configtest.LocalProxyEnv(t, uc, "v1.2.3")...)
|
|
|
|
now := time.Now()
|
|
execProg(t, telemetryDir, "setmode", now.Add(-365*24*time.Hour)) // back-date telemetry acceptance
|
|
execProg(t, telemetryDir, "inc", now.Add(-8*24*time.Hour)) // increment the counter
|
|
|
|
// Populate three weeks of counters to upload.
|
|
for i := -28; i < -7; i++ { // Populate three weeks of counters to upload.
|
|
execProg(t, telemetryDir, "inc", now.Add(time.Duration(i)*24*time.Hour))
|
|
}
|
|
|
|
// Run start concurrently.
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 10; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
execProg(t, telemetryDir, "start", now, uploadEnv...)
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
// Expect exactly three weeks to be uploaded.
|
|
if got, want := len(uploads), 3; got != want {
|
|
t.Errorf("got %d report dates, want %d", got, want)
|
|
}
|
|
for asof, n := range uploads {
|
|
if n != 1 {
|
|
t.Errorf("got %d reports for %s, want 1", n, asof)
|
|
}
|
|
}
|
|
}
|