Skip to content
quarry Artifact [354ba119a0]
Login

Artifact [354ba119a0]

Artifact 354ba119a01e3c31a135f3509155255051015ff43bb7a57955c415d08b4616e3:


package obs

import (
	"context"
	"reflect"
	"testing"
	"time"
)

// The strip's lines are an hour of each signal, a bin a minute, kept as
// events happen rather than read back off a ring whose span depends on
// traffic. Quiet minutes are zero, not absent, and the hour is the
// hour: the sixty-first minute pushes the first one out.
func TestSignalsKeepAnHourOfMinutes(t *testing.T) {
	var s signalRing
	s.render(t0, false)
	s.render(t0.Add(10*time.Second), true)
	s.running(t0.Add(20*time.Second), 3)
	s.running(t0.Add(30*time.Second), 1)
	// Two quiet minutes pass; the third has one render.
	s.render(t0.Add(3*time.Minute), false)
	got := s.snapshot(t0.Add(3*time.Minute + 5*time.Second))
	if got.Minutes != 4 {
		t.Fatalf("FAIL: %d minutes, want 4: %+v", got.Minutes, got)
	}
	if want := []int{2, 0, 0, 1}; !reflect.DeepEqual(got.Renders, want) {
		t.Errorf("FAIL: renders %v, want %v", got.Renders, want)
	}
	if want := []int{1, 0, 0, 0}; !reflect.DeepEqual(got.Slow, want) {
		t.Errorf("FAIL: slow %v, want %v", got.Slow, want)
	}
	// Busy is the most running at once in the minute, not a count.
	if want := []int{3, 0, 0, 0}; !reflect.DeepEqual(got.Busy, want) {
		t.Errorf("FAIL: busy %v, want %v", got.Busy, want)
	}
	// A snapshot taken later fills the quiet minutes to now.
	later := s.snapshot(t0.Add(70 * time.Minute))
	if later.Minutes != SignalMinutes {
		t.Errorf("FAIL: after 70 minutes the ring holds %d, want %d", later.Minutes, SignalMinutes)
	}
	if later.Renders[len(later.Renders)-1] != 0 || sumInts(later.Renders) != 0 {
		t.Errorf("FAIL: the hour after the events should be empty: %v", later.Renders)
	}
}

// The default recorder feeds the ring; a private recorder in a test
// does not, so one test's renders cannot leak into another's hour.
func TestTheDefaultRecorderFeedsTheSignals(t *testing.T) {
	resetSignals()
	defer resetSignals()
	now := time.Now()
	Default.RecordRender(Render{At: now, Path: "GET /x", Elapsed: Budget + time.Millisecond})
	NewRecorder(10, nil).RecordRender(Render{At: now, Path: "GET /y", Elapsed: time.Millisecond})
	got := SignalsNow()
	if got.Minutes == 0 || got.Renders[len(got.Renders)-1] != 1 || got.Slow[len(got.Slow)-1] != 1 {
		t.Errorf("FAIL: signals after one slow render on Default: %+v", got)
	}
}

func sumInts(xs []int) int {
	n := 0
	for _, x := range xs {
		n += x
	}
	return n
}

// A subprocess started under a request names the address it serves;
// one started under a background activity names that; one with neither
// names nothing rather than guessing.
func TestCauseIsTheAddressElseTheReason(t *testing.T) {
	req := WithAddress(WithReason(context.Background(), ReasonWarm), "GET", "/r/fossil/timeline")
	if got := Cause(req); got != "GET /r/fossil/timeline" {
		t.Errorf("FAIL: a request's cause reads %q", got)
	}
	if got := Cause(WithReason(context.Background(), ReasonIngest)); got != ReasonIngest {
		t.Errorf("FAIL: a background cause reads %q", got)
	}
	if got := Cause(context.Background()); got != "" {
		t.Errorf("FAIL: no cause reads %q", got)
	}
}