mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
The module already called itself github.com/luxfi/node while containing no node in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and the Go column of the matrix measured a VM plugin. A repo that fetches the thing it claims to be has not replaced it, so the Go sources move in — 1515 files, the daemon and everything under it — and the build reaches outside this module for nothing. Rust and C++ sit beside it unchanged and are still measured against it by the differential. That is the point of one repo: the reference implementation and the two ports are the same checkout, so parity is a thing you can run rather than a thing you assert. Verified: builds, and the closure carries no lux-private — checked at build time rather than believed. The daemon starts twelve chains on a local network (P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
168 lines
3.5 KiB
Go
168 lines
3.5 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package lock
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestCond(t *testing.T) {
|
|
cancelled, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
|
|
var (
|
|
noop = func(*Cond) {}
|
|
signal = (*Cond).Signal
|
|
broadcast = (*Cond).Broadcast
|
|
merge = func(ops ...func(*Cond)) func(*Cond) {
|
|
return func(c *Cond) {
|
|
for _, op := range ops {
|
|
op(c)
|
|
}
|
|
}
|
|
}
|
|
tests = []struct {
|
|
name string
|
|
ctx context.Context
|
|
expectedErrors []error
|
|
next []func(*Cond)
|
|
}{
|
|
{
|
|
name: "signal_once",
|
|
ctx: context.Background(),
|
|
expectedErrors: make([]error, 1),
|
|
next: []func(*Cond){
|
|
signal,
|
|
},
|
|
},
|
|
{
|
|
name: "signal_twice",
|
|
ctx: context.Background(),
|
|
expectedErrors: make([]error, 1),
|
|
next: []func(*Cond){
|
|
merge(
|
|
signal,
|
|
signal,
|
|
),
|
|
},
|
|
},
|
|
{
|
|
name: "signal_both_once",
|
|
ctx: context.Background(),
|
|
expectedErrors: make([]error, 2),
|
|
next: []func(*Cond){
|
|
signal,
|
|
signal,
|
|
},
|
|
},
|
|
{
|
|
name: "signal_both_once_atomically",
|
|
ctx: context.Background(),
|
|
expectedErrors: make([]error, 2),
|
|
next: []func(*Cond){
|
|
merge(
|
|
signal,
|
|
signal,
|
|
),
|
|
noop,
|
|
},
|
|
},
|
|
{
|
|
name: "broadcast_once",
|
|
ctx: context.Background(),
|
|
expectedErrors: make([]error, 2),
|
|
next: []func(*Cond){
|
|
broadcast,
|
|
noop,
|
|
},
|
|
},
|
|
{
|
|
name: "broadcast_twice",
|
|
ctx: context.Background(),
|
|
expectedErrors: make([]error, 2),
|
|
next: []func(*Cond){
|
|
broadcast,
|
|
broadcast,
|
|
},
|
|
},
|
|
{
|
|
name: "cancelled",
|
|
ctx: cancelled,
|
|
expectedErrors: []error{
|
|
context.Canceled,
|
|
context.Canceled,
|
|
},
|
|
next: []func(*Cond){
|
|
noop,
|
|
noop,
|
|
},
|
|
},
|
|
}
|
|
timeout = time.After(time.Second)
|
|
)
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var (
|
|
wg sync.WaitGroup // WaitGroup to ensure all goroutines have exited before the test ends
|
|
require = require.New(t)
|
|
c = NewCond(&sync.Mutex{})
|
|
errs = make(chan error, len(test.expectedErrors))
|
|
)
|
|
|
|
wg.Add(len(test.expectedErrors))
|
|
defer wg.Wait()
|
|
|
|
for i := range test.expectedErrors {
|
|
// By grabbing the lock outside of the goroutine, we are ensured
|
|
// that all goroutines are waiting after this for loop exits
|
|
// once the lock is no longer held.
|
|
c.L.Lock()
|
|
go func() {
|
|
defer wg.Done()
|
|
t.Log("waiting", i)
|
|
errs <- c.Wait(test.ctx)
|
|
t.Log("waited", i)
|
|
c.L.Unlock()
|
|
}()
|
|
}
|
|
|
|
c.L.Lock()
|
|
t.Log("synchronized waiters")
|
|
c.L.Unlock()
|
|
|
|
// All goroutines are waiting on the condition variable.
|
|
|
|
for i, next := range test.next {
|
|
t.Log("calling next", i)
|
|
next(c)
|
|
t.Log("called next", i)
|
|
|
|
select {
|
|
case err := <-errs:
|
|
require.ErrorIs(err, test.expectedErrors[i])
|
|
t.Log("checked error", i)
|
|
|
|
// Timing out rather than depending on the test timeout allows
|
|
// the t.Logs to be printed rather than a stack trace.
|
|
case <-timeout:
|
|
t.Log("error checking timeout", i)
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
// If the test has passed, this lock shouldn't be needed. But it is
|
|
// included to ensure the test doesn't panic if a timeout has fired.
|
|
c.m.Lock()
|
|
defer c.m.Unlock()
|
|
|
|
require.Empty(c.w)
|
|
})
|
|
}
|
|
}
|