Condivisione della tecnologia

Test di esempio di Go-Knowledge Test

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Si consiglia di leggere prima: https://blog.csdn.net/a18792721831/article/details/140062769

Meccanismo di test di Go-Knowledge

1. Definizione

Il test di esempio deve garantire che il file di test termini con_test.goFINE.
Il metodo di prova deve essereExampleXxxinizio.
I file di test possono trovarsi nella stessa directory del codice sorgente o in una directory separata.
Rileva il formato di output a riga singola come // Output: <stringa prevista>
Il formato di rilevamento dell'output su più righe è // Output: n <stringa prevista> n <stringa prevista> n, ciascuna stringa prevista occupa una riga
Il formato di rilevamento dell'output non ordinato è // Output non ordinato: n <stringa prevista> n <stringa prevista> n <stringa prevista>, ciascuna stringa prevista occupa una riga
La stringa di prova ignorerà automaticamente gli spazi bianchi prima e dopo la stringa.
Se non è presente alcuna rappresentazione di output nella funzione di test, la funzione di test non verrà eseguita.

2. Esempio

Nell'output della stringa, potrebbe essere una riga, potrebbero essere più righe o potrebbe non essere in ordine.
La funzione è la seguente:

func Hello() {
	fmt.Println("Hello")
}

func HelloTwo() {
	fmt.Println("Hello")
	fmt.Println("hi")
}

func HelloMore() {
	m := make(map[string]string)
	m["hello"] = "Hello"
	m["hi"] = "hi"
	m["你好"] = "你好"
	for _, v := range m {
		fmt.Println(v)
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Quindi utilizzare il test di esempio

func ExampleHello() {
	Hello()
	// Output: Hello
}

func ExampleHelloTwo() {
	HelloTwo()
	// Output:
	// Hello
	// hi
}

func ExampleHelloMore() {
	HelloMore()
	// Unordered output:
	// Hello
	// hi
	// 你好
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

utilizzogo test -v Esegui test di esempio, -v indica i risultati di output della console
Inserisci qui la descrizione dell'immagine

3. Struttura dei dati

Ogni test ha una struttura dati per trasportarlo dopo la compilazione. Per il test di esempio, è InternalExample:

type InternalExample struct {
	Name      string // 测试名称
	F         func() // 测试函数
	Output    string // 期望字符串
	Unordered bool // 输出是否无序
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Ad esempio, il caso seguente:

func ExampleHelloMore() {
	HelloMore()
	// Unordered output:
	// Hello
	// hi
	// 你好
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Dopo la compilazione, i membri della struttura dati

InternalExample.Name = "ExampleHelloMore"
InternalExample.F = ExampleHelloMore()
InternalExample.Output = "hellonhin你好n"
InternalExample.Unordered = true
  • 1
  • 2
  • 3
  • 4

4. Processo di compilazione

Nell'articolo: Meccanismo di test di Go-Knowledge

Meccanismo di test di Go-Knowledge

, sappiamo che durante la compilazione verrà chiamatosrc/cmd/go/internal/load/test.go:528
Inserisci qui la descrizione dell'immagine

Sotto carico, verranno elaborati separatamente quattro tipi di test: test unitario, test delle prestazioni, test principale e test del campione.
Inserisci qui la descrizione dell'immagine

Durante l'elaborazione del file di test, verificare se il commento contiene output
Inserisci qui la descrizione dell'immagine

E incapsulare i metadati
Inserisci qui la descrizione dell'immagine

Una volta incapsulati, i metadati verranno utilizzati per eseguire il rendering del modello, generare la voce Main e allo stesso tempo eseguire il rendering della sezione InternalExample.
Inserisci qui la descrizione dell'immagine

5. Processo di esecuzione

Una volta eseguito, verrà eseguitotesting.M.Run, verrà eseguito in RunrunExamples
Inserisci qui la descrizione dell'immagine

func runExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ran, ok bool) {
	ok = true
	var eg InternalExample
	// 对每个实例测试进行执行
	for _, eg = range examples {
	    // 是否匹配
		matched, err := matchString(*match, eg.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %sn", err)
			os.Exit(1)
		}
		if !matched {
			continue
		}
		ran = true
		// 执行
		if !runExample(eg) {
			ok = false
		}
	}
	return ran, ok
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
func runExample(eg InternalExample) (ok bool) {
    // 附加输出
	if *chatty {
		fmt.Printf("=== RUN   %sn", eg.Name)
	}
	// 获取标准输出
	stdout := os.Stdout
	// 新建管道,将标准输出拷贝一份
	r, w, err := os.Pipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	os.Stdout = w
	// 创建一个管道,用于接收标准输出返回的字符串
	outC := make(chan string)
	// 在一个单独的 goroutine 中处理拷贝的输出
	go func() {
		var buf strings.Builder
		_, err := io.Copy(&buf, r)
		r.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: copying pipe: %vn", err)
			os.Exit(1)
		}
		outC <- buf.String()
	}()
	finished := false
	start := time.Now()
	// 在延迟函数中,读取管道中的数据
	defer func() {
		timeSpent := time.Since(start)
		w.Close()
		os.Stdout = stdout
		// 获取标准输出
		out := <-outC
		err := recover()
		// 调用 processRunResult 进行比较 
		ok = eg.processRunResult(out, timeSpent, finished, err)
	}()
	// 执行示例函数,也就是目标函数
	eg.F()
	finished = true
	return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
func (eg *InternalExample) processRunResult(stdout string, timeSpent time.Duration, finished bool, recovered interface{}) (passed bool) {
	passed = true
	dstr := fmtDuration(timeSpent)
	var fail string
	// 标准输出,去除空字符,这也是为何实例测试中会忽略空白字符
	got := strings.TrimSpace(stdout)
	// 期望输出
	want := strings.TrimSpace(eg.Output)
	// 是否乱序
	if eg.Unordered {
	    // 先排序,然后字符串比较
		if sortLines(got) != sortLines(want) && recovered == nil {
			fail = fmt.Sprintf("got:n%snwant (unordered):n%sn", stdout, eg.Output)
		}
	} else {
		if got != want && recovered == nil {
			fail = fmt.Sprintf("got:n%snwant:n%sn", got, want)
		}
	}
	if fail != "" || !finished || recovered != nil {
		fmt.Printf("--- FAIL: %s (%s)n%s", eg.Name, dstr, fail)
		passed = false
	} else if *chatty {
		fmt.Printf("--- PASS: %s (%s)n", eg.Name, dstr)
	}
	if recovered != nil {
		// Propagate the previously recovered result, by panicking.
		panic(recovered)
	}
	if !finished && recovered == nil {
		panic(errNilPanicOrGoexit)
	}
	return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34