PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
源码索引 / CURRENT IMPLEMENTATION

learning/go-patterns/stages/05-parallel/main_test.go

80 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。

跳到函数 / 声明
package main

import (
	"context"
	"errors"
	"testing"
	"time"
)

func waitForSignal(t *testing.T, signal <-chan struct{}, name string) {
	t.Helper()
	select {
	case <-signal:
	case <-time.After(time.Second):
		t.Fatalf("timed out waiting for %s", name)
	}
}

func TestRunTwoStartsBothTasksBeforeCollectingResults(t *testing.T) {
	firstStarted := make(chan struct{})
	secondStarted := make(chan struct{})
	release := make(chan struct{})

	first := func(context.Context) (string, error) {
		close(firstStarted)
		<-release
		return "first", nil
	}
	second := func(context.Context) (string, error) {
		close(secondStarted)
		<-release
		return "second", nil
	}

	done := make(chan struct{})
	var values [2]string
	var err error
	go func() {
		values, err = RunTwo(context.Background(), first, second)
		close(done)
	}()

	waitForSignal(t, firstStarted, "first task to start")
	waitForSignal(t, secondStarted, "second task to start")
	close(release)
	waitForSignal(t, done, "RunTwo to return")

	if err != nil {
		t.Fatalf("RunTwo returned error: %v", err)
	}
	if values != [2]string{"first", "second"} {
		t.Fatalf("results = %#v, want [first second]", values)
	}
}

func TestRunTwoWaitsForTheOtherTaskAfterAnError(t *testing.T) {
	wantErr := errors.New("first task failed")
	secondDone := make(chan struct{})

	values, err := RunTwo(
		context.Background(),
		func(context.Context) (string, error) { return "", wantErr },
		func(context.Context) (string, error) {
			close(secondDone)
			return "second", nil
		},
	)

	if !errors.Is(err, wantErr) {
		t.Fatalf("error = %v, want %v", err, wantErr)
	}
	select {
	case <-secondDone:
	default:
		t.Fatal("RunTwo returned before the second task completed")
	}
	if values[1] != "second" {
		t.Fatalf("second result = %q, want %q", values[1], "second")
	}
}