Obsah
1. Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)
2. Nejdůležitější vlastnosti knihovny goexpect
3. Základní způsob použití knihovny goexpect
4. Informace vracené metodou Expect
5. Chování skriptu ve chvíli, kdy se na výstupu aplikace neobjeví očekávaný text
6. Ovládání interaktivní hry skriptem
8. Zpracování výsledků činnosti metody ExpectBatch
9. Ovládání interpretru Pythonu pomocí dávkových příkazů
10. Zjištění, která verze Pythonu je spuštěna
11. Objekt Caser – rozvětvení na základě vstupu
12. Složitější příklad založený na objektu Caser
13. Kanál obsahující stav aplikace po jejím ukončení
14. Interpret ukončený s návratovou hodnotou odlišnou od nuly
15. Využití knihovny goexpect v jednotkových testech
16. Složitější test založený na dávkové úloze a testu návratové hodnoty testované aplikace
18. Obsah následující části seriálu
19. Repositář s demonstračními příklady
1. Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)
V předchozí části seriálu o programovacím jazyku Go jsme se seznámili s dvojicí knihoven určených právě pro Go. Jednalo se o knihovny nazvané go-expect a gexpect. Jak již název těchto knihoven naznačuje, slouží pro automatizaci práce s aplikacemi ovládanými interaktivně z příkazového řádku (konzole); pro tento účel byl původně vyvinut nástroj expect naprogramovaný v jazyku Tcl (ten je již mnoho let za vrcholem své popularity). Nástrojů využívajících interaktivní dialog na příkazové řádce pochopitelně existuje celá řada; mezi typické zástupce patří ssh, ftp, telnet, gdb, ale i prakticky všechny interpretry programovacích jazyků. Kromě automatizace různých procesů je možné knihovny go-expect a gexpect použít i pro testování aplikací, protože je možné ověřit, jak aplikace odpovídají na předem známý vstup či naopak na vstup náhodně či spíše pseudonáhodně generovaný (fuzzy testy).
Dnes se seznámíme se třetí a současně i poslední knihovnou spadající do stejné kategorie, jako obě knihovny předchozí. Popíšeme si totiž základní způsoby použití knihovny nazvané goexpect, kterou naleznete na adrese https://github.com/google/goexpect a za jejímž vývojem částečně stojí přímo společnost Google (podobně jako za vznikem a podporou samotného programovacího jazyka Go).
2. Nejdůležitější vlastnosti knihovny goexpect
Ze všech tří popisovaných knihoven se právě goexpect nejvíce přibližuje možnostem původního nástroje expect (který je podle mého názoru dosti nedoceněný, za což pravděpodobně může zvolený programovací jazyk). V knihovně goexpect totiž nalezneme jak základní funkce typu „očekávám výstup z aplikace“ a „pošli text na vstup aplikace“, tak i například podporu pro rozvětvení na základě toho, jaké texty aplikace vytiskla a dokonce i podporu pro dávkové příkazy (většinou sekvenci operací pro kontrolu vypsaných textů a zápis nových příkazů). I s těmito možnostmi se postupně seznámíme v navazujících kapitolách.
Knihovnu goexpect nainstalujeme naprosto stejným způsobem, jako jiné balíčky určené pro ekosystém programovacího jazyka Go – použitím příkazu go get:
$ go get github.com/google/goexpect
3. Základní způsob použití knihovny goexpect
Podívejme se nyní ve stručnosti na základní způsob použití knihovny goexpect. Aby bylo možné porovnat přístupy použité ve všech třech popisovaných knihovnách, bude dnešní první demonstrační příklad odvozen od příkladů, které jsme si ukázali minule. Spustíme v něm standardní nástroj uname a zjistíme, zda se na jeho výstupu objevil řetězec „Linux“. Celý postup je relativně přímočarý.
Nejprve je nutné nástroj uname spustit, což se provede jediným příkazem Spawn (následovaným pochopitelně běžnou kontrolou chyb, které při spouštění mohou nastat):
child, _, err := expect.Spawn("uname", -1) if err != nil { log.Fatal(err) }
Dále je důležité zajistit, aby se proces uzavřel na konci celého testu. V tomto případě nám velmi dobře poslouží blok defer:
defer child.Close()
Samotný test, jaký výstup aplikace vyprodukovala, se provádí metodou nazvanou přímočaře Expect, ovšem s tím rozdílem (oproti oběma předchozím knihovnám), že se nespecifikuje holý text, ale regulární výraz. Ten se sestaví a přeloží funkcí regexp.MustCompile (pochopitelně nesmíme zapomenout na import příslušného balíčku):
linuxRe := regexp.MustCompile("Linux") child.Expect(linuxRe, time.Second)
Úplný zdrojový kód dnešního prvního demonstračního příkladu naleznete na adrese https://github.com/tisnik/go-root/blob/master/article44/01_check_uname_linux.go:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("uname", -1) if err != nil { log.Fatal(err) } defer child.Close() linuxRe := regexp.MustCompile("Linux") child.Expect(linuxRe, time.Second) }
console, err := expect.NewConsole(expect.WithStdout(os.Stdout)) if err != nil { log.Fatal(err) } defer console.Close() command := exec.Command("python") command.Stdin = console.Tty() command.Stdout = console.Tty() command.Stderr = console.Tty()
4. Informace vracené metodou Expect
V předchozím demonstračním příkladu jsme volali metodu Expect bez toho, aby došlo k ověření jejích návratových hodnot:
child.Expect(linuxRe, time.Second)
Ve skutečnosti je však prakticky vždy nutné návratové hodnoty nějakým způsobem zpracovat. Tato funkce vrací tři hodnoty – nalezený řetězec, řez (slice) se všemi odpovídajícími částmi textu (využijeme ho u složitějších regulárních výrazů se skupinami – groups) a objekt představující chybu. Pokud k chybě nedošlo, bude poslední vrácená hodnota rovna nil. Tu můžeme snadno zpracovat a pokud k chybě nedošlo vypsat první dvě návratové hodnoty:
s, match, err := child.Expect(linuxRe, time.Second) if err != nil { log.Fatal(err) } log.Printf("Found: %s", s) log.Printf("Matches: %v", match)
Výsledkem by po spuštění mělo být:
2019/11/26 21:30:08 Found: Linux 2019/11/26 21:30:08 Matches: [Linux]
Podobného výsledku dosáhneme i při použití složitějšího regulárního výrazu:
linuxRe := regexp.MustCompile("[Ll][Ii][Nn][Uu][Xx]") s, match, err := child.Expect(linuxRe, time.Second) if err != nil { log.Fatal(err) }
Popř.:
linuxRe := regexp.MustCompile("[A-Za-z]+") s, match, err := child.Expect(linuxRe, time.Second) if err != nil { log.Fatal(err) }
Výsledek by měl být ve všech případech podobný:
2019/11/26 21:48:52 Found: Linux 2019/11/26 21:48:52 Matches: [Linux]
Opět si ukažme úplný zdrojový kód druhého demonstračního příkladu, který naleznete na adrese https://github.com/tisnik/go-root/blob/master/article44/02_check_uname_linux2.go:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("uname", -1) if err != nil { log.Fatal(err) } defer child.Close() linuxRe := regexp.MustCompile("Linux") s, match, err := child.Expect(linuxRe, time.Second) if err != nil { log.Fatal(err) } log.Printf("Found: %s", s) log.Printf("Matches: %v", match) }
5. Chování skriptu ve chvíli, kdy se na výstupu aplikace neobjeví očekávaný text
Můžeme si pochopitelně vyzkoušet, co se stane ve chvíli, kdy na výstupu spuštěné aplikace očekáváme nějaký text, který se ovšem vůbec neobjeví. Podobně jako minule upravíme předchozí příklad takovým způsobem, aby se očekával text „BSD“ a nikoli „Linux“:
linuxRe := regexp.MustCompile("BSD") s, match, err := child.Expect(linuxRe, time.Second)
Po přibližně sekundovém čekání by se měla vypsat zpráva:
expect: Process not running
Tato zpráva nám říká, že testovaný proces (nástroj uname) byl ukončen, ale knihovna goexpect stále nemá k dispozici požadovaný text, což znamená, že ho aplikace ve skutečnosti vůbec nevypsala.
Takto upravený demonstrační příklad naleznete na adrese https://github.com/tisnik/go-root/blob/master/article44/03_check_uname_bsd.go:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("uname", -1) if err != nil { log.Fatal(err) } defer child.Close() linuxRe := regexp.MustCompile("BSD") s, match, err := child.Expect(linuxRe, time.Second) if err != nil { log.Fatal(err) } log.Printf("Found: %s", s) log.Printf("Matches: %v", match) }
6. Ovládání interaktivní hry skriptem
Zjištění, zda se na výstupu aplikace objevil zadaný text, již umíme naprogramovat. Zbývá maličkost – poslat aplikaci (na její standardní vstup) nějaký text. V knihovně goexpect pro tento účel slouží metoda nazvaná Send. Nesmíme přitom zapomenout, že v mnoha aplikacích je nutné příkazy ukončit Enterem, což je v unixových systémech znak „\n“:
child.Send("d\n")
Jednoduchý skript pro ovládání minule zmíněné hry Zombie MUD lze vytvořit takto (viz též https://github.com/tisnik/go-root/blob/master/article44/04_telnet_game_A.go):
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("telnet zombiemud.org", -1) if err != nil { log.Fatal(err) } defer child.Close() s, _, err := child.Expect(regexp.MustCompile("Your choice or name:"), 2*time.Second) if err != nil { log.Fatal(err) } log.Println(s) // ukonceni hry child.Send("d\n") s, _, err = child.Expect(regexp.MustCompile("Ok, see you later!"), 2*time.Second) if err != nil { log.Fatal(err) } log.Println(s) }
7. Dávkové příkazy
Další užitečnou součástí knihovny goexpect jsou takzvané „dávkové příkazy“ (batch), které nám umožňují zjednodušit sérii volání metod Expect a Send. Namísto toho lze zapsat:
... ... ... &expect.BExp{R: "Your choice or name:"}, &expect.BSnd{S: "d\n"}, &expect.BExp{R: "Ok, see you later!"}, ... ... ...
První řádek odpovídá volání metody Expect, druhý volání metody Send atd. Celá série takto vytvořených příkazů je typu řez (slice) hodnot expect.Batcher, kterou spustíme metodou ExpectBatch:
_, err = child.ExpectBatch([]expect.Batcher{ &expect.BExp{R: "Your choice or name:"}, &expect.BSnd{S: "d\n"}, &expect.BExp{R: "Ok, see you later!"}}, 1*time.Second)
Testovat je nutné především druhou návratovou hodnotu, která buď obsahuje nil nebo objekt představující chybu:
if err != nil { log.Fatal(err) }
Příklad z předchozí kapitoly lze tedy zkrátit takto:
package main import ( "log" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("telnet zombiemud.org", 1*time.Second) if err != nil { log.Fatal(err) } defer child.Close() _, err = child.ExpectBatch([]expect.Batcher{ &expect.BExp{R: "Your choice or name:"}, &expect.BSnd{S: "d\n"}, &expect.BExp{R: "Ok, see you later!"}}, 1*time.Second) if err != nil { log.Fatal(err) } log.Println("OK") }
8. Zpracování výsledků činnosti metody ExpectBatch
Z předchozího textu již víme, že druhou návratovou hodnotou metody ExpectBatch je nil nebo struktura představující chybový stav. První návratová hodnota je však zajímavější, protože obsahuje (zjednodušeně řečeno) historii všech příkazů volaných v rámci jednoho dávkového procesu. Můžeme si to snadno odzkoušet:
br, err := child.ExpectBatch([]expect.Batcher{ &expect.BExp{R: "Your choice or name:"}, &expect.BSnd{S: "d\n"}, &expect.BExp{R: "Ok, see you later!"}}, 2*time.Second)
Hodnota uložená do proměnné br je řez struktur s výsledky volání jednotlivých příkazů. Zobrazit si můžeme některý z prvků těchto struktur, především samotný výstup zachycený z testované/řízené aplikace:
for _, b := range br { log.Println(b.Idx, b.Output) }
Výstup z příkladu může vypadat například takto:
2019/11/26 21:43:10 OK 2019/11/26 21:43:10 0 Trying 85.23.110.31... Connected to zombiemud.org. Escape character is '^]'. Welcome to ... ___ __ __) __ __) ______ (, ) /) , (, /| /| (, / / (, / ) / ______ (/_ _ / | / | / / / / _/_(_) // (_/_) _(__(/_) / |/ |_ / / _/___ /_ ) / (_/ ' (___(_ (_/___ / (__ / ... online since 1994. There are currently 57 mortals and 6 wizards online. Give me your name or choose one of the following: [C]reate a new character [W]ho is playing [V]isit the game [S]tatus of the game [D]isconnect Your choice or name: 2019/11/26 21:43:10 2 d Ok, see you later!
Vždy je zobrazen index (0, 2, …) a příslušný text.
Opět si pro úplnost ukažme úplný kód příkladu, který jsme použili pro získání předchozího výsledku:
package main import ( "log" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("telnet zombiemud.org", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() br, err := child.ExpectBatch([]expect.Batcher{ &expect.BExp{R: "Your choice or name:"}, &expect.BSnd{S: "d\n"}, &expect.BExp{R: "Ok, see you later!"}}, 2*time.Second) if err != nil { log.Fatal(err) } log.Println("OK") for _, b := range br { log.Println(b.Idx, b.Output) } }
9. Ovládání interpretru Pythonu pomocí dávkových příkazů
V předchozím článku jsme si mj. ukázali ovládání interpretru Pythonu s využitím následujících příkazů (resp. jejich sekvencí):
console.SendLine("1+2") console.ExpectString("3") console.ExpectString(">>> ") console.SendLine("6*7") console.ExpectString("42") console.ExpectString(">>> ") console.SendLine("quit()")
Tuto sekvenci lze s využitím knihovny goexpect zkrátit na:
&expect.BSnd{S: "1+2\n"}, &expect.BExp{R: "3"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "6*7\n"}, &expect.BExp{R: "42"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "quit()\n"}},
Zařazení výše uvedené sekvence příkazů do skriptu může vypadat následovně:
package main import ( "log" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("python", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() br, err := child.ExpectBatch([]expect.Batcher{ &expect.BExp{R: ">>> "}, &expect.BSnd{S: "1+2\n"}, &expect.BExp{R: "3"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "6*7\n"}, &expect.BExp{R: "42"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "quit()\n"}}, 2*time.Second) if err != nil { log.Fatal(err) } log.Println("OK") for _, b := range br { log.Println(b.Output) } }
10. Zjištění, která verze Pythonu je spuštěna
V dalším demonstračním příkladu je ukázán jeden ze způsobů zjištění, jaká verze Pythonu (Python 2, Python 3) je aktuálně spuštěna, což je opět varianta na příklady prezentované minule. Nyní ovšem můžeme využít regulární výrazy a navíc lze přímo zpracovat jejich výsledek (capture). Povšimněte si, že v regulárním výrazu je definovaná skupina (group) okolo předpokládaného čísla verze:
_, m, err := child.Expect(regexp.MustCompile("Python ([23])"), 2*time.Second)
V případě, že je verze Pythonu nalezena (err == nil), pak bude v proměnné m uložena dvojice řetězců: celý text odpovídající regulárnímu výrazu a text s číslem verze. Pak tedy můžeme přímo přistoupit ke druhému řetězci:
version := m[1] log.Println("Python version:", version)
Úplný kód skriptu, který verzi detekuje, lze napsat následovně:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("python", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() _, m, err := child.Expect(regexp.MustCompile("Python ([23])"), 2*time.Second) err = child.Send("quit()\n") if err != nil { log.Fatal(err) } version := m[1] log.Println("Python version:", version) }
11. Objekt Caser – rozvětvení na základě vstupu
V původní knihovně expect bylo relativně snadné provést rozvětvení na základě vstupu přečteného z terminálu běžící aplikace. Podobnou funkcionalitu nám nabízí i knihovna goexpect, ovšem ne v tak čitelné podobě, což je mimo jiné způsobeno i silným typovým systémem programovacího jazyka Go (oproti netypovému TCL). Rozvětvení na základě toho, zda se na terminálu objevil text „Python 2“ nebo „Python 3“, může být zapsáno takto:
&expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}}, time.Second)
Tento test je možné relativně snadno zakomponovat do skriptu:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("python", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() br, err := child.ExpectBatch([]expect.Batcher{ &expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}}, time.Second) err = child.Send("quit()\n") if err != nil { log.Fatal(err) } log.Println("OK") for _, b := range br { log.Println(b.Output) } }
12. Složitější příklad založený na objektu Caser
Skript ovšem může být složitější a kromě podmínek (i vnořených!) může obsahovat nám již známé příkazy pro očekávání textu na terminálu aplikace a pro poslání jiného textu na její vstup. Ostatně si to můžeme ukázat na dalším příkladu:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, _, err := expect.Spawn("python", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() br, err := child.ExpectBatch([]expect.Batcher{ &expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "6*7\n"}, &expect.BExp{R: "42"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "quit()\n"}}, 2*time.Second) log.Println("OK") for _, b := range br { log.Println(b.Output) } }
Díky tomu, že tento příklad vypisuje výsledek volání ExpectBatch, budeme moci sledovat činnost celého skriptu:
2019/11/27 20:08:37 OK 2019/11/27 20:08:37 Python 2.7.6 (default, Nov 23 2017, 15:49:48) [GCC 4.8.4] on linux2 Type "help", "copyright", "credits" or "license" for more information. 2019/11/27 20:08:37 >>> 2019/11/27 20:08:37 42 2019/11/27 20:08:37 >>>
13. Kanál obsahující stav aplikace po jejím ukončení
Prozatím jsme používali metodu Spawn takovým způsobem, že jsme ignorovali její druhou návratovou hodnotu:
child, _, err := expect.Spawn("python", 2*time.Second)
Ve druhé hodnotě je ve skutečnosti vrácen kanál, z něhož je možné přečíst stav aplikace po jejím ukončení. Připomeňme si, že čtení z kanálu (bez kapacity) je blokující operací, takže se dá využít i pro čekání na ukončení aplikace. Ovšem nás dnes bude zajímat především informace, která je do kanálu poslána ve chvíli, kdy se aplikace ukončila. Kanál tedy uložíme do proměnné:
child, errChannel, err := expect.Spawn("python", 2*time.Second)
A na konci (po ukončení interpretru) data z kanálu přečteme a zpracujeme:
err = <-errChannel if err != nil { log.Fatal(err) } log.Println("Exit: success")
Výsledek by měl vypadat následovně:
2019/11/26 21:41:36 OK 2019/11/26 21:41:36 Exit: success
Pro úplnost si ukažme celý zdrojový kód takto upraveného demonstračního příkladu:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, errChannel, err := expect.Spawn("python", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() _, err = child.ExpectBatch([]expect.Batcher{ &expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "import sys\n"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "sys.exit(0)\n"}}, 2*time.Second) log.Println("OK") err = <-errChannel if err != nil { log.Fatal(err) } log.Println("Exit: success") }
14. Interpret ukončený s návratovou hodnotou odlišnou od nuly
Nyní interpret Pythonu ukončíme zavoláním funkce sys.exit(1):
... ... ... &expect.BSnd{S: "sys.exit(1)\n"}}, ... ... ...
V tomto případě by se měl náš skript chovat odlišně:
2019/11/26 21:41:48 OK 2019/11/26 21:41:48 exit status 1 exit status 1
Opět si pro úplnost ukažme zdrojový kód celého příkladu, který vypadá takto:
package main import ( "log" "regexp" "time" "github.com/google/goexpect" ) func main() { child, errChannel, err := expect.Spawn("python", 2*time.Second) if err != nil { log.Fatal(err) } defer child.Close() _, err = child.ExpectBatch([]expect.Batcher{ &expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "import sys\n"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "sys.exit(1)\n"}}, 2*time.Second) log.Println("OK") err = <-errChannel if err != nil { log.Fatal(err) } log.Println("Exit: success") }
15. Využití knihovny goexpect v jednotkových testech
Velmi užitečné může být využití knihovny goexpect v jednotkových testech, které jsou většinou postaveny přímo na standardním balíčku testing (ovšem pochopitelně je možné v případě potřeby použít i další balíčky). Podívejme se, jak snadno je možné přepsat příklad, v němž se komunikuje s interpretrem programovacího jazyka Python, do podoby jednotkového testu. Namísto funkce main použijeme libovolnou funkci, jejíž název odpovídá požadavkům jednotkových testů (jméno začíná Text a parametrem je hodnota typu *testing.T):
func TestPythonInterpreter(t *testing.T) { ... ... ... }
Poté již můžeme volat metody implementované typem testing.T, tj. t.Fatal(), t.Error() či t.Log().
Úplný zdrojový text takto přepsaného příkladu vypadá následovně:
package main import ( "regexp" "testing" "time" "github.com/google/goexpect" ) func TestPythonInterpreter(t *testing.T) { child, _, err := expect.Spawn("python", 2*time.Second) if err != nil { t.Fatal(err) } t.Log("Python interpreter has been started") defer child.Close() _, m, err := child.Expect(regexp.MustCompile("Python ([23])"), 2*time.Second) err = child.Send("quit()\n") if err != nil { t.Fatal(err) } if len(m) < 1 { t.Fatal("No match (should not happen") } version := m[1] t.Log("Detected Python version:", version) }
Po spuštění příkladu by se na výstupu měly objevit informace o tom, jaká verze Pythonu byla detekována; následně se jen zobrazí PASS značící, že byl test úspěšně dokončen:
=== RUN TestPythonInterpreter --- PASS: TestPythonInterpreter (0.02s) 13_python_test.go:16: Python interpreter has been started 13_python_test.go:31: Detected Python version: 2 PASS ok command-line-arguments 0.029s
16. Složitější test založený na dávkové úloze a testu návratové hodnoty testované aplikace
Naprosto stejným způsobem, jaký byl uveden v předchozí kapitole, lze přepsat i výše uvedený příklad, který po spuštění interpretru Pythonu provede několik aritmetických výpočtů a následně interpret ukončí zavoláním funkce sys.exit(0). Opět při implementaci využijeme standardní balíček testing:
package main import ( "regexp" "testing" "time" "github.com/google/goexpect" ) func TestPythonInterpreter(t *testing.T) { child, errChannel, err := expect.Spawn("python", 2*time.Second) if err != nil { t.Fatal(err) } t.Log("Python interpreter has been started") defer child.Close() _, err = child.ExpectBatch([]expect.Batcher{ &expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "import sys\n"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "sys.exit(0)\n"}}, 2*time.Second) t.Log("OK") err = <-errChannel if err != nil { t.Fatal(err) } t.Log("Exit: success") }
Test je nutné spustit příkazem go test -v. Po spuštění by se měly na terminálu objevit následující zprávy ukazující jak průběh celého testu, tak i jeho konečný výsledek:
=== RUN TestPythonInterpreter --- PASS: TestPythonInterpreter (0.03s) 14_error_channel_test.go:16: Python interpreter has been started 14_error_channel_test.go:30: OK 14_error_channel_test.go:36: Exit: success PASS ok command-line-arguments (cached)
17. Chování jednotkového testu ve chvíli, kdy je interpret ukončen s návratovou hodnotou odlišnou od nuly
Ukažme si ještě pro úplnost, jak se chování jednotkového testu změní v případě, že testovaná aplikace (konkrétně interpret Pythonu) skončí s návratovým kódem odlišným od nuly. V kódu příkladu provedeme následující minimální změnu:
... ... ... &expect.BSnd{S: "sys.exit(1)\n"}}, ... ... ...
Výsledek získaný po spuštění jednotkového testu:
=== RUN TestPythonInterpreter --- FAIL: TestPythonInterpreter (0.03s) 15_error_channel_test.go:16: Python interpreter has been started 15_error_channel_test.go:30: OK 15_error_channel_test.go:34: exit status 1 FAIL FAIL command-line-arguments 0.036s
Úplný zdrojový kód tohoto demonstračního příkladu naleznete na adrese https://github.com/tisnik/go-root/blob/master/article44/15_error_channel_test.go:
package main import ( "regexp" "testing" "time" "github.com/google/goexpect" ) func TestPythonInterpreter(t *testing.T) { child, errChannel, err := expect.Spawn("python", 2*time.Second) if err != nil { t.Fatal(err) } t.Log("Python interpreter has been started") defer child.Close() _, err = child.ExpectBatch([]expect.Batcher{ &expect.BCas{[]expect.Caser{ &expect.Case{R: regexp.MustCompile("Python 2"), T: expect.OK()}, &expect.Case{R: regexp.MustCompile("Python 3"), T: expect.OK()}}}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "import sys\n"}, &expect.BExp{R: ">>> "}, &expect.BSnd{S: "sys.exit(1)\n"}}, 2*time.Second) t.Log("OK") err = <-errChannel if err != nil { t.Fatal(err) } t.Log("Exit: success") }
18. Obsah následující části seriálu
Již několik částí tohoto seriálu bylo věnováno problematice testování, ať již přímo psaní testů pro aplikace vyvinuté v jazyku Go (jednotkové testy, BDD), nebo použití Go pro testování REST API, testování aplikací s textovým uživatelským rozhraním atd. Tomuto důležitému tématu se budeme věnovat i příště, protože si popíšeme velmi zajímavý nástroj nazvaný GΩmega.
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně pět až šest megabajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Go is on a Trajectory to Become the Next Enterprise Programming Language
https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 10 tools written in Go that every developer needs to know
https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go