package waiterr_test import ( "errors" "testing" "testing/synctest" "github.com/nalgeon/be" "codeberg.org/danjones000/waiterr" ) func TestGo(t *testing.T) { we := waiterr.New() err := errors.New("uh-oh") var run bool we.Go(func() error { run = true return err }) be.Err(t, we.Wait(), err) be.True(t, run) } func TestWait(t *testing.T) { we := waiterr.New() er1 := errors.New("uh-oh") er2 := errors.New("oops") we.Go(func() error { return er1 }) we.Go(func() error { return nil }) we.Go(func() error { return er2 }) err := we.Wait() be.Err(t, err, er1, er2) if ers, ok := err.(interface{ Unwrap() []error }); ok { all := ers.Unwrap() be.Equal(t, len(all), 2) be.True(t, all[0] == er1 || all[0] == er2) be.True(t, all[1] == er2 || all[1] == er1) } else { t.Fatal("Returned error should have Unwrap method") } } func TestWaitForError(tt *testing.T) { tt.Run("first error", func(t *testing.T) { we := waiterr.New() er1 := errors.New("uh-oh") er2 := errors.New("oops") we.Go(func() error { return nil }) we.Go(func() error { return er1 }) we.Go(func() error { return er2 }) err := we.WaitForError() // Due to how goroutines run, it is possible that either of those return first. This is an acceptable limitation be.True(t, err == er1 || err == er2) }) tt.Run("no error", func(t *testing.T) { we := waiterr.New() we.Go(func() error { return nil }) we.Go(func() error { return nil }) we.Go(func() error { return nil }) err := we.WaitForError() be.Err(t, err, nil) }) tt.Run("first error set", func(tt2 *testing.T) { we := waiterr.New() expectedErr := errors.New("pre-set error") synctest.Test(tt2, func(t *testing.T) { we.Go(func() error { return expectedErr }) // synctest.Wait ensures that the gorouting has finished before anything else. synctest.Wait() we.Go(func() error { return errors.New("another error") }) synctest.Wait() we.Go(func() error { return nil }) actualErr := we.WaitForError() be.Err(t, actualErr, expectedErr) }) }) } func TestUnwrap(tt *testing.T) { tt.Run("two errors", func(t *testing.T) { we := waiterr.New() er1 := errors.New("error one") er2 := errors.New("error two") we.Go(func() error { return er1 }) we.Go(func() error { return nil }) we.Go(func() error { return er2 }) we.Go(func() error { return nil }) _ = we.Wait() // Ensure all goroutines complete unwrapped := we.Unwrap() be.Equal(t, len(unwrapped), 2) be.True(t, (unwrapped[0] == er1 && unwrapped[1] == er2) || (unwrapped[0] == er2 && unwrapped[1] == er1)) }) tt.Run("no errors", func(t *testing.T) { weNoErr := waiterr.New() weNoErr.Go(func() error { return nil }) weNoErr.Go(func() error { return nil }) _ = weNoErr.Wait() be.Equal(t, weNoErr.Unwrap(), nil) }) }