mirror of
https://github.com/logos-messaging/nim-sds.git
synced 2026-01-31 12:23:25 +00:00
removing old go wrappers
This commit is contained in:
parent
415d0d8a42
commit
d9c3bccc31
281
sds_wrapper.go
281
sds_wrapper.go
@ -1,281 +0,0 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I${SRCDIR}/library
|
||||
#cgo LDFLAGS: -L${SRCDIR}/build -llibsds
|
||||
#cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/build
|
||||
|
||||
#include <stdlib.h> // For C.free
|
||||
#include "library/libsds.h" // Update include path
|
||||
|
||||
// Forward declaration for the single Go callback relay function
|
||||
extern void globalCallbackRelay(void* handle, CEventType eventType, void* data1, void* data2, size_t data3);
|
||||
|
||||
// Helper function to call the C memory freeing functions
|
||||
static void callFreeCResultError(CResult res) { FreeCResultError(res); }
|
||||
static void callFreeCWrapResult(CWrapResult res) { FreeCWrapResult(res); }
|
||||
static void callFreeCUnwrapResult(CUnwrapResult res) { FreeCUnwrapResult(res); }
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// --- Go Types ---
|
||||
|
||||
// ReliabilityManagerHandle represents the opaque handle to the Nim object
|
||||
type ReliabilityManagerHandle unsafe.Pointer
|
||||
|
||||
// MessageID is a type alias for string for clarity
|
||||
type MessageID string
|
||||
|
||||
// Callbacks holds the Go functions to be called by the Nim library
|
||||
type Callbacks struct {
|
||||
OnMessageReady func(messageId MessageID)
|
||||
OnMessageSent func(messageId MessageID)
|
||||
OnMissingDependencies func(messageId MessageID, missingDeps []MessageID)
|
||||
OnPeriodicSync func()
|
||||
}
|
||||
|
||||
// Global map to store callbacks associated with handles
|
||||
var (
|
||||
callbackRegistry = make(map[ReliabilityManagerHandle]*Callbacks)
|
||||
registryMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// --- Go Wrapper Functions ---
|
||||
|
||||
// NewReliabilityManager creates a new instance of the Nim ReliabilityManager
|
||||
func NewReliabilityManager(channelId string) (ReliabilityManagerHandle, error) {
|
||||
cChannelId := C.CString(channelId)
|
||||
defer C.free(unsafe.Pointer(cChannelId))
|
||||
|
||||
handle := C.NewReliabilityManager(cChannelId)
|
||||
if handle == nil {
|
||||
// Note: Nim side currently just prints to stdout on creation failure
|
||||
return nil, errors.New("failed to create ReliabilityManager (check Nim logs/stdout)")
|
||||
}
|
||||
return ReliabilityManagerHandle(handle), nil
|
||||
}
|
||||
|
||||
// CleanupReliabilityManager frees the resources associated with the handle
|
||||
func CleanupReliabilityManager(handle ReliabilityManagerHandle) {
|
||||
if handle == nil {
|
||||
return
|
||||
}
|
||||
registryMutex.Lock()
|
||||
delete(callbackRegistry, handle)
|
||||
registryMutex.Unlock()
|
||||
C.CleanupReliabilityManager(unsafe.Pointer(handle))
|
||||
}
|
||||
|
||||
// ResetReliabilityManager resets the state of the manager
|
||||
func ResetReliabilityManager(handle ReliabilityManagerHandle) error {
|
||||
if handle == nil {
|
||||
return errors.New("handle is nil")
|
||||
}
|
||||
cResult := C.ResetReliabilityManager(unsafe.Pointer(handle))
|
||||
if !cResult.is_ok {
|
||||
errMsg := C.GoString(cResult.error_message)
|
||||
C.callFreeCResultError(cResult) // Free the error message
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WrapOutgoingMessage wraps a message with reliability metadata
|
||||
func WrapOutgoingMessage(handle ReliabilityManagerHandle, message []byte, messageId MessageID) ([]byte, error) {
|
||||
if handle == nil {
|
||||
return nil, errors.New("handle is nil")
|
||||
}
|
||||
cMessageId := C.CString(string(messageId))
|
||||
defer C.free(unsafe.Pointer(cMessageId))
|
||||
|
||||
var cMessagePtr unsafe.Pointer
|
||||
if len(message) > 0 {
|
||||
cMessagePtr = C.CBytes(message) // C.CBytes allocates memory that needs to be freed
|
||||
defer C.free(cMessagePtr)
|
||||
} else {
|
||||
cMessagePtr = nil
|
||||
}
|
||||
cMessageLen := C.size_t(len(message))
|
||||
|
||||
cWrapResult := C.WrapOutgoingMessage(unsafe.Pointer(handle), cMessagePtr, cMessageLen, cMessageId)
|
||||
|
||||
if !cWrapResult.base_result.is_ok {
|
||||
errMsg := C.GoString(cWrapResult.base_result.error_message)
|
||||
C.callFreeCWrapResult(cWrapResult) // Free error and potentially allocated message
|
||||
return nil, errors.New(errMsg)
|
||||
}
|
||||
|
||||
// Copy the wrapped message from C memory to Go slice
|
||||
// Explicitly cast the message pointer to unsafe.Pointer
|
||||
wrappedMessage := C.GoBytes(unsafe.Pointer(cWrapResult.message), C.int(cWrapResult.message_len))
|
||||
C.callFreeCWrapResult(cWrapResult) // Free the C-allocated message buffer
|
||||
|
||||
return wrappedMessage, nil
|
||||
}
|
||||
|
||||
// UnwrapReceivedMessage unwraps a received message
|
||||
func UnwrapReceivedMessage(handle ReliabilityManagerHandle, message []byte) ([]byte, []MessageID, error) {
|
||||
if handle == nil {
|
||||
return nil, nil, errors.New("handle is nil")
|
||||
}
|
||||
|
||||
var cMessagePtr unsafe.Pointer
|
||||
if len(message) > 0 {
|
||||
cMessagePtr = C.CBytes(message)
|
||||
defer C.free(cMessagePtr)
|
||||
} else {
|
||||
cMessagePtr = nil
|
||||
}
|
||||
cMessageLen := C.size_t(len(message))
|
||||
|
||||
cUnwrapResult := C.UnwrapReceivedMessage(unsafe.Pointer(handle), cMessagePtr, cMessageLen)
|
||||
|
||||
if !cUnwrapResult.base_result.is_ok {
|
||||
errMsg := C.GoString(cUnwrapResult.base_result.error_message)
|
||||
C.callFreeCUnwrapResult(cUnwrapResult) // Free error and potentially allocated fields
|
||||
return nil, nil, errors.New(errMsg)
|
||||
}
|
||||
|
||||
// Copy unwrapped message content
|
||||
unwrappedContent := C.GoBytes(unsafe.Pointer(cUnwrapResult.message), C.int(cUnwrapResult.message_len))
|
||||
|
||||
// Copy missing dependencies
|
||||
missingDeps := make([]MessageID, cUnwrapResult.missing_deps_count)
|
||||
if cUnwrapResult.missing_deps_count > 0 {
|
||||
// Convert C array of C strings to Go slice of strings
|
||||
cDepsArray := (*[1 << 30]*C.char)(unsafe.Pointer(cUnwrapResult.missing_deps))[:cUnwrapResult.missing_deps_count:cUnwrapResult.missing_deps_count]
|
||||
for i, s := range cDepsArray {
|
||||
missingDeps[i] = MessageID(C.GoString(s))
|
||||
}
|
||||
}
|
||||
|
||||
C.callFreeCUnwrapResult(cUnwrapResult) // Free C-allocated message, deps array, and strings
|
||||
|
||||
return unwrappedContent, missingDeps, nil
|
||||
}
|
||||
|
||||
// MarkDependenciesMet informs the library that dependencies are met
|
||||
func MarkDependenciesMet(handle ReliabilityManagerHandle, messageIDs []MessageID) error {
|
||||
if handle == nil {
|
||||
return errors.New("handle is nil")
|
||||
}
|
||||
if len(messageIDs) == 0 {
|
||||
return nil // Nothing to do
|
||||
}
|
||||
|
||||
// Convert Go string slice to C array of C strings (char**)
|
||||
cMessageIDs := make([]*C.char, len(messageIDs))
|
||||
for i, id := range messageIDs {
|
||||
cMessageIDs[i] = C.CString(string(id))
|
||||
defer C.free(unsafe.Pointer(cMessageIDs[i])) // Ensure each CString is freed
|
||||
}
|
||||
|
||||
// Create a pointer (**C.char) to the first element of the slice
|
||||
var cMessageIDsPtr **C.char
|
||||
if len(cMessageIDs) > 0 {
|
||||
cMessageIDsPtr = &cMessageIDs[0]
|
||||
} else {
|
||||
cMessageIDsPtr = nil // Handle empty slice case
|
||||
}
|
||||
|
||||
// Pass the pointer variable (cMessageIDsPtr) directly, which is of type **C.char
|
||||
cResult := C.MarkDependenciesMet(unsafe.Pointer(handle), cMessageIDsPtr, C.size_t(len(messageIDs)))
|
||||
|
||||
if !cResult.is_ok {
|
||||
errMsg := C.GoString(cResult.error_message)
|
||||
C.callFreeCResultError(cResult)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterCallback sets the single Go callback relay function
|
||||
func RegisterCallback(handle ReliabilityManagerHandle, callbacks Callbacks) error {
|
||||
if handle == nil {
|
||||
return errors.New("handle is nil")
|
||||
}
|
||||
|
||||
// Store the Go callbacks associated with this handle
|
||||
registryMutex.Lock()
|
||||
callbackRegistry[handle] = &callbacks
|
||||
registryMutex.Unlock()
|
||||
|
||||
// Register the single global Go relay function with the Nim library
|
||||
// Nim will call globalCallbackRelay, passing the handle as the first argument.
|
||||
C.RegisterCallback(
|
||||
unsafe.Pointer(handle),
|
||||
(C.CEventCallback)(C.globalCallbackRelay), // Pass the Go relay function pointer
|
||||
nil, // user_data is not used here, handle is passed directly by Nim wrapper
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartPeriodicTasks starts the background tasks in the Nim library
|
||||
func StartPeriodicTasks(handle ReliabilityManagerHandle) error {
|
||||
if handle == nil {
|
||||
return errors.New("handle is nil")
|
||||
}
|
||||
C.StartPeriodicTasks(unsafe.Pointer(handle))
|
||||
// Assuming StartPeriodicTasks doesn't return an error status in C API
|
||||
return nil
|
||||
}
|
||||
|
||||
// globalCallbackRelay is called by Nim for all events.
|
||||
// It uses the handle to find the correct Go Callbacks struct and dispatch the call.
|
||||
//
|
||||
//export globalCallbackRelay
|
||||
func globalCallbackRelay(handle unsafe.Pointer, eventType C.CEventType, data1 unsafe.Pointer, data2 unsafe.Pointer, data3 C.size_t) {
|
||||
goHandle := ReliabilityManagerHandle(handle)
|
||||
|
||||
registryMutex.RLock()
|
||||
callbacks, ok := callbackRegistry[goHandle]
|
||||
registryMutex.RUnlock()
|
||||
|
||||
if !ok || callbacks == nil {
|
||||
fmt.Printf("Go: globalCallbackRelay: No callbacks registered for handle %v\n", goHandle) // Uncommented
|
||||
return
|
||||
}
|
||||
|
||||
// Use a goroutine to avoid blocking the Nim thread
|
||||
go func() {
|
||||
switch eventType {
|
||||
case C.EVENT_MESSAGE_READY:
|
||||
if callbacks.OnMessageReady != nil {
|
||||
msgIdStr := C.GoString((*C.char)(data1))
|
||||
callbacks.OnMessageReady(MessageID(msgIdStr))
|
||||
}
|
||||
case C.EVENT_MESSAGE_SENT:
|
||||
if callbacks.OnMessageSent != nil {
|
||||
msgIdStr := C.GoString((*C.char)(data1))
|
||||
callbacks.OnMessageSent(MessageID(msgIdStr))
|
||||
}
|
||||
case C.EVENT_MISSING_DEPENDENCIES:
|
||||
if callbacks.OnMissingDependencies != nil {
|
||||
msgIdStr := C.GoString((*C.char)(data1))
|
||||
depsCount := int(data3)
|
||||
deps := make([]MessageID, depsCount)
|
||||
if depsCount > 0 {
|
||||
// Convert C array of C strings (**char) to Go slice
|
||||
cDepsArray := (*[1 << 30]*C.char)(data2)[:depsCount:depsCount]
|
||||
for i, s := range cDepsArray {
|
||||
deps[i] = MessageID(C.GoString(s))
|
||||
}
|
||||
}
|
||||
callbacks.OnMissingDependencies(MessageID(msgIdStr), deps)
|
||||
}
|
||||
case C.EVENT_PERIODIC_SYNC:
|
||||
if callbacks.OnPeriodicSync != nil {
|
||||
callbacks.OnPeriodicSync()
|
||||
}
|
||||
default:
|
||||
fmt.Printf("Go: globalCallbackRelay: Received unknown event type %d for handle %v\n", eventType, goHandle)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -1,259 +0,0 @@
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test basic creation, cleanup, and reset
|
||||
func TestLifecycle(t *testing.T) {
|
||||
channelID := "test-lifecycle"
|
||||
handle, err := NewReliabilityManager(channelID)
|
||||
if err != nil {
|
||||
t.Fatalf("NewReliabilityManager failed: %v", err)
|
||||
}
|
||||
if handle == nil {
|
||||
t.Fatal("NewReliabilityManager returned a nil handle")
|
||||
}
|
||||
defer CleanupReliabilityManager(handle) // Ensure cleanup even on test failure
|
||||
|
||||
err = ResetReliabilityManager(handle)
|
||||
if err != nil {
|
||||
t.Errorf("ResetReliabilityManager failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test wrapping and unwrapping a simple message
|
||||
func TestWrapUnwrap(t *testing.T) {
|
||||
channelID := "test-wrap-unwrap"
|
||||
handle, err := NewReliabilityManager(channelID)
|
||||
if err != nil {
|
||||
t.Fatalf("NewReliabilityManager failed: %v", err)
|
||||
}
|
||||
defer CleanupReliabilityManager(handle)
|
||||
|
||||
originalPayload := []byte("hello reliability")
|
||||
messageID := MessageID("msg-wrap-1")
|
||||
|
||||
wrappedMsg, err := WrapOutgoingMessage(handle, originalPayload, messageID)
|
||||
if err != nil {
|
||||
t.Fatalf("WrapOutgoingMessage failed: %v", err)
|
||||
}
|
||||
if len(wrappedMsg) == 0 {
|
||||
t.Fatal("WrapOutgoingMessage returned empty bytes")
|
||||
}
|
||||
|
||||
// Simulate receiving the wrapped message
|
||||
unwrappedPayload, missingDeps, err := UnwrapReceivedMessage(handle, wrappedMsg)
|
||||
if err != nil {
|
||||
t.Fatalf("UnwrapReceivedMessage failed: %v", err)
|
||||
}
|
||||
|
||||
if string(unwrappedPayload) != string(originalPayload) {
|
||||
t.Errorf("Unwrapped payload mismatch: got %q, want %q", unwrappedPayload, originalPayload)
|
||||
}
|
||||
if len(missingDeps) != 0 {
|
||||
t.Errorf("Expected 0 missing dependencies, got %d: %v", len(missingDeps), missingDeps)
|
||||
}
|
||||
}
|
||||
|
||||
// Test dependency handling
|
||||
func TestDependencies(t *testing.T) {
|
||||
channelID := "test-deps"
|
||||
handle, err := NewReliabilityManager(channelID)
|
||||
if err != nil {
|
||||
t.Fatalf("NewReliabilityManager failed: %v", err)
|
||||
}
|
||||
defer CleanupReliabilityManager(handle)
|
||||
|
||||
// 1. Send message 1 (will become a dependency)
|
||||
payload1 := []byte("message one")
|
||||
msgID1 := MessageID("msg-dep-1")
|
||||
wrappedMsg1, err := WrapOutgoingMessage(handle, payload1, msgID1)
|
||||
if err != nil {
|
||||
t.Fatalf("WrapOutgoingMessage (1) failed: %v", err)
|
||||
}
|
||||
// Simulate receiving msg1 to add it to history (implicitly acknowledges it)
|
||||
_, _, err = UnwrapReceivedMessage(handle, wrappedMsg1)
|
||||
if err != nil {
|
||||
t.Fatalf("UnwrapReceivedMessage (1) failed: %v", err)
|
||||
}
|
||||
|
||||
// 2. Send message 2 (depends on message 1 implicitly via causal history)
|
||||
payload2 := []byte("message two")
|
||||
msgID2 := MessageID("msg-dep-2")
|
||||
wrappedMsg2, err := WrapOutgoingMessage(handle, payload2, msgID2)
|
||||
if err != nil {
|
||||
t.Fatalf("WrapOutgoingMessage (2) failed: %v", err)
|
||||
}
|
||||
|
||||
// 3. Create a new manager to simulate a different peer receiving msg2 without msg1
|
||||
handle2, err := NewReliabilityManager(channelID) // Same channel ID
|
||||
if err != nil {
|
||||
t.Fatalf("NewReliabilityManager (2) failed: %v", err)
|
||||
}
|
||||
defer CleanupReliabilityManager(handle2)
|
||||
|
||||
// 4. Unwrap message 2 on the second manager - should report msg1 as missing
|
||||
_, missingDeps, err := UnwrapReceivedMessage(handle2, wrappedMsg2)
|
||||
if err != nil {
|
||||
t.Fatalf("UnwrapReceivedMessage (2) on handle2 failed: %v", err)
|
||||
}
|
||||
|
||||
if len(missingDeps) == 0 {
|
||||
t.Fatalf("Expected missing dependencies, got none")
|
||||
}
|
||||
foundDep1 := false
|
||||
for _, dep := range missingDeps {
|
||||
if dep == msgID1 {
|
||||
foundDep1 = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundDep1 {
|
||||
t.Errorf("Expected missing dependency %q, got %v", msgID1, missingDeps)
|
||||
}
|
||||
|
||||
// 5. Mark the dependency as met
|
||||
err = MarkDependenciesMet(handle2, []MessageID{msgID1})
|
||||
if err != nil {
|
||||
t.Fatalf("MarkDependenciesMet failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test callbacks
|
||||
func TestCallbacks(t *testing.T) {
|
||||
channelID := "test-callbacks"
|
||||
handle, err := NewReliabilityManager(channelID)
|
||||
if err != nil {
|
||||
t.Fatalf("NewReliabilityManager failed: %v", err)
|
||||
}
|
||||
defer CleanupReliabilityManager(handle)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
receivedReady := make(map[MessageID]bool)
|
||||
receivedSent := make(map[MessageID]bool)
|
||||
receivedMissing := make(map[MessageID][]MessageID)
|
||||
syncRequested := false
|
||||
var cbMutex sync.Mutex // Protect access to callback tracking maps/vars
|
||||
|
||||
callbacks := Callbacks{
|
||||
OnMessageReady: func(messageId MessageID) {
|
||||
fmt.Printf("Test: OnMessageReady received: %s\n", messageId)
|
||||
cbMutex.Lock()
|
||||
receivedReady[messageId] = true
|
||||
cbMutex.Unlock()
|
||||
wg.Done()
|
||||
},
|
||||
OnMessageSent: func(messageId MessageID) {
|
||||
fmt.Printf("Test: OnMessageSent received: %s\n", messageId)
|
||||
cbMutex.Lock()
|
||||
receivedSent[messageId] = true
|
||||
cbMutex.Unlock()
|
||||
wg.Done()
|
||||
},
|
||||
OnMissingDependencies: func(messageId MessageID, missingDeps []MessageID) {
|
||||
fmt.Printf("Test: OnMissingDependencies received for %s: %v\n", messageId, missingDeps)
|
||||
cbMutex.Lock()
|
||||
receivedMissing[messageId] = missingDeps
|
||||
cbMutex.Unlock()
|
||||
wg.Done()
|
||||
},
|
||||
OnPeriodicSync: func() {
|
||||
fmt.Println("Test: OnPeriodicSync received")
|
||||
cbMutex.Lock()
|
||||
syncRequested = true
|
||||
cbMutex.Unlock()
|
||||
// Don't wg.Done() here, it might be called multiple times
|
||||
},
|
||||
}
|
||||
|
||||
err = RegisterCallback(handle, callbacks)
|
||||
if err != nil {
|
||||
t.Fatalf("RegisterCallback failed: %v", err)
|
||||
}
|
||||
|
||||
// Start tasks AFTER registering callbacks
|
||||
err = StartPeriodicTasks(handle)
|
||||
if err != nil {
|
||||
t.Fatalf("StartPeriodicTasks failed: %v", err)
|
||||
}
|
||||
|
||||
// --- Test Scenario ---
|
||||
|
||||
// 1. Send msg1
|
||||
wg.Add(1) // Expect OnMessageSent for msg1 eventually
|
||||
payload1 := []byte("callback test 1")
|
||||
msgID1 := MessageID("cb-msg-1")
|
||||
wrappedMsg1, err := WrapOutgoingMessage(handle, payload1, msgID1)
|
||||
if err != nil {
|
||||
t.Fatalf("WrapOutgoingMessage (1) failed: %v", err)
|
||||
}
|
||||
|
||||
// 2. Receive msg1 (triggers OnMessageReady for msg1, OnMessageSent for msg1 via causal history)
|
||||
wg.Add(1) // Expect OnMessageReady for msg1
|
||||
_, _, err = UnwrapReceivedMessage(handle, wrappedMsg1)
|
||||
if err != nil {
|
||||
t.Fatalf("UnwrapReceivedMessage (1) failed: %v", err)
|
||||
}
|
||||
|
||||
// 3. Send msg2 (depends on msg1)
|
||||
wg.Add(1) // Expect OnMessageSent for msg2 eventually
|
||||
payload2 := []byte("callback test 2")
|
||||
msgID2 := MessageID("cb-msg-2")
|
||||
wrappedMsg2, err := WrapOutgoingMessage(handle, payload2, msgID2)
|
||||
if err != nil {
|
||||
t.Fatalf("WrapOutgoingMessage (2) failed: %v", err)
|
||||
}
|
||||
|
||||
// 4. Receive msg2 (triggers OnMessageReady for msg2, OnMessageSent for msg2)
|
||||
wg.Add(1) // Expect OnMessageReady for msg2
|
||||
_, _, err = UnwrapReceivedMessage(handle, wrappedMsg2)
|
||||
if err != nil {
|
||||
t.Fatalf("UnwrapReceivedMessage (2) failed: %v", err)
|
||||
}
|
||||
|
||||
// --- Verification ---
|
||||
// Wait for expected callbacks with a timeout
|
||||
waitTimeout(&wg, 5*time.Second, t)
|
||||
|
||||
cbMutex.Lock()
|
||||
defer cbMutex.Unlock()
|
||||
|
||||
if !receivedReady[msgID1] {
|
||||
t.Errorf("OnMessageReady not called for %s", msgID1)
|
||||
}
|
||||
if !receivedReady[msgID2] {
|
||||
t.Errorf("OnMessageReady not called for %s", msgID2)
|
||||
}
|
||||
if !receivedSent[msgID1] {
|
||||
t.Errorf("OnMessageSent not called for %s", msgID1)
|
||||
}
|
||||
if !receivedSent[msgID2] {
|
||||
t.Errorf("OnMessageSent not called for %s", msgID2)
|
||||
}
|
||||
// We didn't explicitly test missing deps in this path
|
||||
if len(receivedMissing) > 0 {
|
||||
t.Errorf("Unexpected OnMissingDependencies calls: %v", receivedMissing)
|
||||
}
|
||||
// Periodic sync is harder to guarantee in a short test, just check if it was ever true
|
||||
if !syncRequested {
|
||||
t.Logf("Warning: OnPeriodicSync might not have been called within the test timeout")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to wait for WaitGroup with a timeout
|
||||
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration, t *testing.T) {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
wg.Wait()
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
// Completed normally
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("Timed out waiting for callbacks")
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user