Hlavní navigace

Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)

Pavel Tišnovský

Dnes dokončíme téma, kterému jsme se začali věnovat minule. Jedná se o popis možností knihoven nahrazujících nástroj expect původně naprogramovaný Tcl. Dnes si popíšeme knihovnu goexpect pocházejících přímo od Googlu.

Doba čtení: 38 minut

Sdílet

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

17. Chování jednotkového testu ve chvíli, kdy je interpret ukončen s návratovou hodnotou odlišnou od nuly

18. Obsah následující části seriálu

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

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)
}
Poznámka: druhá hodnota –1 značí, že se nebude explicitně nastaveno čekání na text, který má aplikace vypsat (timeout). V dalších příkladech však čekání použijeme.

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_chec­k_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)
}
Poznámka: příklady ukázané minule byly, alespoň co se týká počtu zapsaných řádků, poněkud delší a komplikovanější. Ostatně si můžeme porovnat přímočaré zavolání goexpect.Spawn s inicializací vyžadovanou knihovnou go-expect:
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_chec­k_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_chec­k_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_tel­net_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"}},
Poznámka: zda se jedná o řešení jednodušší či naopak méně čitelné, již ponechám na zvážení samotnému čtenáři. Výhodou druhého řešení je, že očekávané řetězce jsou zapsány formou regulárních výrazů.

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)
Poznámka: povšimněte si, že se neuvádí pouze podmínka (regulární výraz), ale i operace, která se má provést při splnění této podmínky.

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
Poznámka: povšimněte si, že se v kanálu objevil nejenom numerický kód (1), ale i celá zpráva „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
Poznámka: pro spuštění je nutné použít příkaz go test, ideálně s přepínačem -v, aby se vypisovaly i podrobnější informace o průběhu testu.

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_e­rror_channel_test.go:

Diners Vánoce 2019

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:

# Příklad Stručný popis Cesta
1 01_check_uname_linux.go spuštění příkazu uname a otestování jeho textového výstupu https://github.com/tisnik/go-root/blob/master/article44/01_chec­k_uname_linux.go
2 02_check_uname_linux2.go podobné předchozímu příkladu, ovšem současně se vytisknou i návratové hodnoty metody Expect https://github.com/tisnik/go-root/blob/master/article44/02_chec­k_uname_linux2.go
3 03_check_uname_bsd.go příklad shodný s příkladem předchozím, ovšem tentokrát se vytiskne neočekávaný text https://github.com/tisnik/go-root/blob/master/article44/03_chec­k_uname_bsd.go
4 04_telnet_game_A.go hra (MUD) ovládaná přes telnet https://github.com/tisnik/go-root/blob/master/article44/04_tel­net_game_A.go
5 05_telnet_game_B.go vylepšení předchozího příkladu https://github.com/tisnik/go-root/blob/master/article44/05_tel­net_game_B.go
6 06_telnet_game_C.go další vylepšení předchozího příkladu https://github.com/tisnik/go-root/blob/master/article44/06_tel­net_game_C.go
7 07_python_interpreter.go ovládání interpretru Pythonu https://github.com/tisnik/go-root/blob/master/article44/07_pyt­hon_interpreter.go
8 08_python_version.go detekce verze Pythonu https://github.com/tisnik/go-root/blob/master/article44/08_pyt­hon_version.go
9 09_caser.go rozvětvení realizované objektem Caser https://github.com/tisnik/go-root/blob/master/article44/09_ca­ser.go
10 10_caser_etc.go rozvětvení realizované objektem Caser https://github.com/tisnik/go-root/blob/master/article44/10_ca­ser_etc.go
11 11_error_channel.go kontrola návratového kódu aplikace https://github.com/tisnik/go-root/blob/master/article44/11_e­rror_channel.go
12 12_error_channel.go kontrola návratového kódu aplikace https://github.com/tisnik/go-root/blob/master/article44/12_e­rror_channel.go
13 13_python_test.go přepis příkladu číslo 7 do formy jednotkového testu https://github.com/tisnik/go-root/blob/master/article44/13_pyt­hon_test.go
14 14_error_channel_test.go přepis příkladu číslo 11 do formy jednotkového testu https://github.com/tisnik/go-root/blob/master/article44/14_e­rror_channel_test.go
15 15_error_channel_test.go přepis příkladu číslo 12 do formy jednotkového testu https://github.com/tisnik/go-root/blob/master/article44/15_e­rror_channel_test.go

20. Odkazy na Internetu

  1. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  2. Balíček goexpect
    https://github.com/google/goexpect
  3. Balíček go-expect
    https://github.com/Netflix/go-expect
  4. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  5. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  6. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  7. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  8. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  9. goblin na GitHubu
    https://github.com/franela/goblin
  10. Mocha framework
    https://mochajs.org/
  11. frisby na GitHubu
    https://github.com/verdverm/frisby
  12. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  13. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  14. Cucumber for golang
    https://github.com/DATA-DOG/godog
  15. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  16. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  17. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  18. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  19. xml.org
    http://www.xml.org/
  20. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  21. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  22. JSON to YAML
    https://www.json2yaml.com/
  23. Data Format Converter
    https://toolkit.site/format.html
  24. Viper na GitHubu
    https://github.com/spf13/viper
  25. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  26. The fantastic ORM library for Golang
    http://gorm.io/
  27. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  28. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  29. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  30. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  31. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  32. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  33. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  34. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  35. Sémantické verzování
    https://semver.org/
  36. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  37. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  38. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  39. Modules
    https://github.com/golang/go/wi­ki/Modules
  40. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  41. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  42. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  43. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  44. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  45. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  46. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  47. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  48. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  49. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  50. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  51. gopls
    https://github.com/golang/go/wi­ki/gopls
  52. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  53. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  54. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  55. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  56. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  57. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  58. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  59. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  60. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  61. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  62. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  63. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  64. Package trace
    https://golang.org/pkg/runtime/trace/
  65. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  66. Command trace
    https://golang.org/cmd/trace/
  67. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  68. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  69. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  70. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  71. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  72. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  73. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  74. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  75. 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/
  76. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  77. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  78. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  79. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  80. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  81. The LLDB Debugger
    http://lldb.llvm.org/
  82. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  83. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  84. 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
  85. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  86. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  87. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  88. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  89. 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/
  90. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  91. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  92. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  93. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  94. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  95. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  96. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  97. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  98. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  99. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  100. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  101. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  102. go-cron
    https://github.com/rk/go-cron
  103. gocron
    https://github.com/jasonlvhit/gocron
  104. clockwork
    https://github.com/whiteShtef/cloc­kwork
  105. clockwerk
    https://github.com/onatm/clockwerk
  106. JobRunner
    https://github.com/bamzi/jobrunner
  107. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  108. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  109. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  110. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  111. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  112. go-prompt
    https://github.com/c-bata/go-prompt
  113. readline
    https://github.com/chzyer/readline
  114. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  115. go-readline
    https://github.com/fiorix/go-readline
  116. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  117. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  118. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  119. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  120. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  121. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  122. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  123. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  124. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  125. Editline Library (libedit)
    http://thrysoee.dk/editline/
  126. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  127. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  128. WinEditLine
    http://mingweditline.sourceforge.net/
  129. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  130. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  131. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  132. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  133. history(3) – Linux man page
    https://linux.die.net/man/3/history
  134. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  135. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  136. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  137. Balíček ogletest
    https://github.com/jacobsa/ogletest
  138. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  139. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  140. package testing
    https://golang.org/pkg/testing/
  141. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  142. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  143. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  144. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  145. GoConvey
    http://goconvey.co/
  146. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  147. 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
  148. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  149. package gg
    https://godoc.org/github.com/fo­gleman/gg
  150. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  151. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  152. The Go image package
    https://blog.golang.org/go-image-package
  153. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  154. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  155. YAML
    https://yaml.org/
  156. edn
    https://github.com/edn-format/edn
  157. Smile
    https://github.com/FasterXML/smile-format-specification
  158. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  159. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  160. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  161. Introducing JSON
    http://json.org/
  162. Package json
    https://golang.org/pkg/encoding/json/
  163. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  164. Go by Example: JSON
    https://gobyexample.com/json
  165. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  166. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  167. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  168. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  169. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  170. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  171. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  172. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  173. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  174. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  175. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  176. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  177. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  178. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  179. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  180. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  181. Algorithms to Go
    https://yourbasic.org/
  182. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  183. 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/
  184. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  185. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  186. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  187. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  188. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  189. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  190. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  191. The Go Programming Language (home page)
    https://golang.org/
  192. GoDoc
    https://godoc.org/
  193. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  194. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  195. The Go Programming Language Specification
    https://golang.org/ref/spec
  196. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  197. Package builtin
    https://golang.org/pkg/builtin/
  198. Package fmt
    https://golang.org/pkg/fmt/
  199. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  200. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  201. Learning Go
    https://www.miek.nl/go/
  202. Go Bootcamp
    http://www.golangbootcamp.com/
  203. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  204. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  205. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  206. The Go Blog
    https://blog.golang.org/
  207. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  208. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  209. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  210. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  211. 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
  212. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  213. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  214. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  215. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  216. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  217. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  218. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  219. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  220. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  221. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  222. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  223. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  224. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  225. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  226. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  227. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  228. 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/
  229. 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
  230. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  231. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  232. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  233. Go vs. Python
    https://www.peterbe.com/plog/govspy
  234. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  235. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  236. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  237. Go by Example: Slices
    https://gobyexample.com/slices
  238. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  239. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  240. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  241. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  242. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  243. nils In Go
    https://go101.org/article/nil.html
  244. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  245. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  246. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  247. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  248. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  249. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  250. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  251. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  252. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  253. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  254. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  255. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  256. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  257. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  258. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  259. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  260. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  261. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  262. 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
  263. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  264. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  265. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  266. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  267. Selectors
    https://golang.org/ref/spec#Selectors
  268. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  269. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  270. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  271. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  272. Part 21: Goroutines
    https://golangbot.com/goroutines/
  273. Part 22: Channels
    https://golangbot.com/channels/
  274. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  275. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  276. 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/
  277. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  278. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  279. Control Structures
    https://www.golang-book.com/books/intro/5
  280. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  281. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  282. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  283. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  284. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  285. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  286. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  287. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  288. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  289. Effective Go
    https://golang.org/doc/ef­fective_go.html
  290. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  291. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  292. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  293. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  294. Nils in Go
    https://www.doxsey.net/blog/nils-in-go