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.
209 lines
4.5 KiB
Go
209 lines
4.5 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package rpcchainvm
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/log"
|
|
)
|
|
|
|
type notifier func(_ context.Context, msg Message) error
|
|
|
|
func (n notifier) Notify(ctx context.Context, msg Message) error {
|
|
return n(ctx, msg)
|
|
}
|
|
|
|
func TestNotifier(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
notifier := notifier(func(_ context.Context, msg Message) error {
|
|
defer wg.Done()
|
|
require.Equal(t, PendingTxs, msg)
|
|
return nil
|
|
})
|
|
|
|
c := make(chan Message)
|
|
|
|
subscriber := func(ctx context.Context) (Message, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
case msg := <-c:
|
|
return msg, nil
|
|
}
|
|
}
|
|
|
|
nf := NewNotificationForwarder(
|
|
Notifier(notifier),
|
|
subscriber,
|
|
&log.NoLog{})
|
|
|
|
defer nf.Close()
|
|
|
|
c <- PendingTxs
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestNotifierStopWhileSubscribing(_ *testing.T) {
|
|
notifier := notifier(func(ctx context.Context, _ Message) error {
|
|
<-ctx.Done()
|
|
return nil
|
|
})
|
|
|
|
var subscribed sync.WaitGroup
|
|
subscribed.Add(1)
|
|
|
|
subscribe := func(ctx context.Context) (Message, error) {
|
|
subscribed.Done()
|
|
<-ctx.Done()
|
|
return 0, nil
|
|
}
|
|
|
|
nf := NewNotificationForwarder(
|
|
Notifier(notifier),
|
|
subscribe,
|
|
&log.NoLog{})
|
|
|
|
subscribed.Wait()
|
|
nf.Close()
|
|
}
|
|
|
|
func TestNotifierWaitForPrefChangeAfterNotify(t *testing.T) {
|
|
var notifiedCount uint32
|
|
|
|
engine := Notifier(notifier(func(_ context.Context, _ Message) error {
|
|
atomic.AddUint32(¬ifiedCount, 1)
|
|
return nil
|
|
}))
|
|
|
|
subscribe := func(context.Context) (Message, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
nf := NewNotificationForwarder(engine, subscribe, &log.NoLog{})
|
|
defer nf.Close()
|
|
|
|
require.Eventually(t, func() bool {
|
|
return atomic.LoadUint32(¬ifiedCount) == 1
|
|
}, time.Minute, 10*time.Millisecond)
|
|
|
|
require.Never(t, func() bool {
|
|
return atomic.LoadUint32(¬ifiedCount) != 1
|
|
}, time.Millisecond*100, 10*time.Millisecond)
|
|
|
|
nf.CheckForEvent()
|
|
|
|
require.Eventually(t, func() bool {
|
|
return atomic.LoadUint32(¬ifiedCount) == 2
|
|
}, time.Minute, 10*time.Millisecond)
|
|
|
|
require.Never(t, func() bool {
|
|
return atomic.LoadUint32(¬ifiedCount) != 2
|
|
}, time.Millisecond*100, 10*time.Millisecond)
|
|
}
|
|
|
|
func TestNotifierReSubscribeAtPrefChange(t *testing.T) {
|
|
notifications := make(chan Message)
|
|
|
|
engine := Notifier(notifier(func(ctx context.Context, msg Message) error {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case notifications <- msg:
|
|
}
|
|
|
|
<-ctx.Done()
|
|
return ctx.Err()
|
|
}))
|
|
|
|
messages := make(chan Message)
|
|
|
|
signal := make(chan struct{})
|
|
|
|
subscriber := func(ctx context.Context) (Message, error) {
|
|
select {
|
|
case <-signal:
|
|
default:
|
|
close(signal)
|
|
<-ctx.Done()
|
|
return 0, ctx.Err()
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
case msg := <-messages:
|
|
return msg, nil
|
|
}
|
|
}
|
|
|
|
nf := NewNotificationForwarder(engine, subscriber, &log.NoLog{})
|
|
defer nf.Close()
|
|
|
|
select {
|
|
case <-signal:
|
|
nf.CheckForEvent()
|
|
case <-time.After(time.Minute):
|
|
require.FailNow(t, "Timed out waiting for the notification forwarder to subscribe")
|
|
}
|
|
|
|
select {
|
|
case messages <- PendingTxs:
|
|
case <-time.After(time.Minute):
|
|
require.FailNow(t, "Timed out waiting to send message to notification forwarder")
|
|
}
|
|
|
|
select {
|
|
case msg := <-notifications:
|
|
require.Equal(t, PendingTxs, msg)
|
|
case <-time.After(time.Minute):
|
|
require.FailNow(t, "Timed out waiting for notification forwarder to forward message")
|
|
}
|
|
}
|
|
|
|
// TestNotifierWithoutLoggerDoesNotPanic pins the forwarder to surviving an
|
|
// unset Log.
|
|
//
|
|
// The constructor spawns the forwarding goroutine itself, so a caller cannot
|
|
// set Log afterwards — passing nil is fatal at the instant of construction.
|
|
// And forwardNotification logs before it does anything else, on a goroutine
|
|
// whose panic no caller can recover, so the nil dereference takes the whole
|
|
// process with it rather than failing one VM.
|
|
func TestNotifierWithoutLoggerDoesNotPanic(t *testing.T) {
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
notifier := notifier(func(_ context.Context, msg Message) error {
|
|
defer wg.Done()
|
|
require.Equal(t, PendingTxs, msg)
|
|
return nil
|
|
})
|
|
|
|
c := make(chan Message)
|
|
subscriber := func(ctx context.Context) (Message, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
case msg := <-c:
|
|
return msg, nil
|
|
}
|
|
}
|
|
|
|
// No logger. The forwarder still forwards.
|
|
nf := NewNotificationForwarder(Notifier(notifier), subscriber, nil)
|
|
defer nf.Close()
|
|
|
|
c <- PendingTxs
|
|
wg.Wait()
|
|
|
|
require.NotNil(t, nf.Log, "an unset Log must be answered before the loop starts")
|
|
}
|