lang: Add per-test config with count maximums

Some of our special tests can only be run once per `go test` invocation.
That is, using the test -count flag will cause a guaranteed failure
since we depend on a global being initialized only once as part of that
test.

This adds a per-test config option so that a user can specify to never
run a particular test more than once. This lets us continue to use the
-count flag with the test suite, without it causing some tests to fail.
This commit is contained in:
James Shubin
2024-01-22 16:53:33 -05:00
parent f0a4a9e3c4
commit d01c168450
5 changed files with 119 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ package lang
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -62,6 +63,24 @@ const (
runGraphviz = false // run graphviz in tests? runGraphviz = false // run graphviz in tests?
) )
var (
testMutex *sync.Mutex // guards testCounter
testCounter map[string]uint // counts how many times each test ran
)
func init() {
testMutex = &sync.Mutex{}
testCounter = make(map[string]uint)
}
// ConfigProperties are some values that are used to specify how each test runs.
type ConfigProperties struct {
// MaximumCount specifies how many times this test can run safely in a
// single iteration. If zero then this means infinite.
MaximumCount uint `json:"maximum-count"`
}
// TestAstFunc1 is a more advanced version which pulls code from physical dirs. // TestAstFunc1 is a more advanced version which pulls code from physical dirs.
func TestAstFunc1(t *testing.T) { func TestAstFunc1(t *testing.T) {
const magicError = "# err: " const magicError = "# err: "
@@ -169,6 +188,7 @@ func TestAstFunc1(t *testing.T) {
// copy files out into the test temp directory // copy files out into the test temp directory
var testOutput []byte var testOutput []byte
var testConfig []byte
found := false found := false
for _, file := range archive.Files { for _, file := range archive.Files {
if file.Name == "OUTPUT" { if file.Name == "OUTPUT" {
@@ -176,6 +196,10 @@ func TestAstFunc1(t *testing.T) {
found = true found = true
continue continue
} }
if file.Name == "CONFIG" {
testConfig = file.Data
continue
}
name := filepath.Join(tmpdir, file.Name) name := filepath.Join(tmpdir, file.Name)
dir := filepath.Dir(name) dir := filepath.Dir(name)
@@ -189,6 +213,29 @@ func TestAstFunc1(t *testing.T) {
} }
} }
var c ConfigProperties // add pointer to get nil if empty
if len(testConfig) > 0 {
if err := json.Unmarshal(testConfig, &c); err != nil {
t.Errorf("err parsing txtar(%s) config: %+v", txtarFile, err)
return
}
}
if testing.Verbose() {
t.Logf("config: %+v", c)
}
testMutex.Lock() // global
count := testCounter[t.Name()] // global
testCounter[t.Name()]++
testMutex.Unlock()
if c.MaximumCount != 0 && count >= c.MaximumCount {
if count == c.MaximumCount { // logf once
t.Logf("Skipping test after count: %d", count)
}
return
}
if !found { // skip missing tests if !found { // skip missing tests
return return
} }
@@ -624,6 +671,7 @@ func TestAstFunc2(t *testing.T) {
// copy files out into the test temp directory // copy files out into the test temp directory
var testOutput []byte var testOutput []byte
var testConfig []byte
found := false found := false
for _, file := range archive.Files { for _, file := range archive.Files {
if file.Name == "OUTPUT" { if file.Name == "OUTPUT" {
@@ -631,6 +679,10 @@ func TestAstFunc2(t *testing.T) {
found = true found = true
continue continue
} }
if file.Name == "CONFIG" {
testConfig = file.Data
continue
}
name := filepath.Join(tmpdir, file.Name) name := filepath.Join(tmpdir, file.Name)
dir := filepath.Dir(name) dir := filepath.Dir(name)
@@ -644,6 +696,29 @@ func TestAstFunc2(t *testing.T) {
} }
} }
var c ConfigProperties // add pointer to get nil if empty
if len(testConfig) > 0 {
if err := json.Unmarshal(testConfig, &c); err != nil {
t.Errorf("err parsing txtar(%s) config: %+v", txtarFile, err)
return
}
}
if testing.Verbose() {
t.Logf("config: %+v", c)
}
testMutex.Lock() // global
count := testCounter[t.Name()] // global
testCounter[t.Name()]++
testMutex.Unlock()
if c.MaximumCount != 0 && count >= c.MaximumCount {
if count == c.MaximumCount { // logf once
t.Logf("Skipping test after count: %d", count)
}
return
}
if !found { // skip missing tests if !found { // skip missing tests
return return
} }
@@ -1404,6 +1479,7 @@ func TestAstFunc3(t *testing.T) {
// copy files out into the test temp directory // copy files out into the test temp directory
var testOutput []byte var testOutput []byte
var testConfig []byte
found := false found := false
for _, file := range archive.Files { for _, file := range archive.Files {
if file.Name == "OUTPUT" { if file.Name == "OUTPUT" {
@@ -1411,6 +1487,10 @@ func TestAstFunc3(t *testing.T) {
found = true found = true
continue continue
} }
if file.Name == "CONFIG" {
testConfig = file.Data
continue
}
name := filepath.Join(tmpdir, file.Name) name := filepath.Join(tmpdir, file.Name)
dir := filepath.Dir(name) dir := filepath.Dir(name)
@@ -1424,6 +1504,29 @@ func TestAstFunc3(t *testing.T) {
} }
} }
var c ConfigProperties // add pointer to get nil if empty
if len(testConfig) > 0 {
if err := json.Unmarshal(testConfig, &c); err != nil {
t.Errorf("err parsing txtar(%s) config: %+v", txtarFile, err)
return
}
}
if testing.Verbose() {
t.Logf("config: %+v", c)
}
testMutex.Lock() // global
count := testCounter[t.Name()] // global
testCounter[t.Name()]++
testMutex.Unlock()
if c.MaximumCount != 0 && count >= c.MaximumCount {
if count == c.MaximumCount { // logf once
t.Logf("Skipping test after count: %d", count)
}
return
}
if !found { // skip missing tests if !found { // skip missing tests
return return
} }

View File

@@ -1,3 +1,7 @@
-- CONFIG --
{
"maximum-count": 1
}
-- main.mcl -- -- main.mcl --
import "test" import "test"
# one_instance_a should only produce one value, and will error if initialized twice # one_instance_a should only produce one value, and will error if initialized twice

View File

@@ -1,3 +1,7 @@
-- CONFIG --
{
"maximum-count": 1
}
-- main.mcl -- -- main.mcl --
import "test" import "test"

View File

@@ -1,3 +1,7 @@
-- CONFIG --
{
"maximum-count": 1
}
-- main.mcl -- -- main.mcl --
import "test" import "test"

View File

@@ -1,3 +1,7 @@
-- CONFIG --
{
"maximum-count": 1
}
-- main.mcl -- -- main.mcl --
import "test" import "test"