Login light
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

package display

import (
    "io"
    "net"
    "os"
    "reflect"
    "sync"
    "testing"

    "gopl.io/ch7/eval"
)

// NOTE: we can't use !+..!- comments to excerpt these tests
// into the book because it defeats the Example mechanism,
// which requires the // Output comment to be at the end
// of the function.

func Example_expr() {
    e, _ := eval.Parse("sqrt(A / pi)")
    Display("e", e)
    // Output:
    // Display e (eval.call):
    // e.fn = "sqrt"
    // e.args[0].type = eval.binary
    // e.args[0].value.op = 47
    // e.args[0].value.x.type = eval.Var
    // e.args[0].value.x.value = "A"
    // e.args[0].value.y.type = eval.Var
    // e.args[0].value.y.value = "pi"
}

func Example_slice() {
    Display("slice", []*int{new(int), nil})
    // Output:
    // Display slice ([]*int):
    // (*slice[0]) = 0
    // slice[1] = nil
}

func Example_nilInterface() {
    var w io.Writer
    Display("w", w)
    // Output:
    // Display w (<nil>):
    // w = invalid
}

func Example_ptrToInterface() {
    var w io.Writer
    Display("&w", &w)
    // Output:
    // Display &w (*io.Writer):
    // (*&w) = nil
}

func Example_struct() {
    Display("x", struct{ x interface{} }{3})
    // Output:
    // Display x (struct { x interface {} }):
    // x.x.type = int
    // x.x.value = 3
}

func Example_interface() {
    var i interface{} = 3
    Display("i", i)
    // Output:
    // Display i (int):
    // i = 3
}

func Example_ptrToInterface2() {
    var i interface{} = 3
    Display("&i", &i)
    // Output:
    // Display &i (*interface {}):
    // (*&i).type = int
    // (*&i).value = 3
}

func Example_array() {
    Display("x", [1]interface{}{3})
    // Output:
    // Display x ([1]interface {}):
    // x[0].type = int
    // x[0].value = 3
}

func Example_movie() {
    //!+movie
    type Movie struct {
        Title, Subtitle string
        Year            int
        Color           bool
        Actor           map[string]string
        Oscars          []string
        Sequel          *string
    }
    //!-movie
    //!+strangelove
    strangelove := Movie{
        Title:    "Dr. Strangelove",
        Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
        Year:     1964,
        Color:    false,
        Actor: map[string]string{
            "Dr. Strangelove":            "Peter Sellers",
            "Grp. Capt. Lionel Mandrake": "Peter Sellers",
            "Pres. Merkin Muffley":       "Peter Sellers",
            "Gen. Buck Turgidson":        "George C. Scott",
            "Brig. Gen. Jack D. Ripper":  "Sterling Hayden",
            `Maj. T.J. "King" Kong`:      "Slim Pickens",
        },

        Oscars: []string{
            "Best Actor (Nomin.)",
            "Best Adapted Screenplay (Nomin.)",
            "Best Director (Nomin.)",
            "Best Picture (Nomin.)",
        },
    }
    //!-strangelove
    Display("strangelove", strangelove)

    // We don't use an Output: comment since displaying
    // a map is nondeterministic.
    /*
        //!+output
        Display strangelove (display.Movie):
        strangelove.Title = "Dr. Strangelove"
        strangelove.Subtitle = "How I Learned to Stop Worrying and Love the Bomb"
        strangelove.Year = 1964
        strangelove.Color = false
        strangelove.Actor["Gen. Buck Turgidson"] = "George C. Scott"
        strangelove.Actor["Brig. Gen. Jack D. Ripper"] = "Sterling Hayden"
        strangelove.Actor["Maj. T.J. \"King\" Kong"] = "Slim Pickens"
        strangelove.Actor["Dr. Strangelove"] = "Peter Sellers"
        strangelove.Actor["Grp. Capt. Lionel Mandrake"] = "Peter Sellers"
        strangelove.Actor["Pres. Merkin Muffley"] = "Peter Sellers"
        strangelove.Oscars[0] = "Best Actor (Nomin.)"
        strangelove.Oscars[1] = "Best Adapted Screenplay (Nomin.)"
        strangelove.Oscars[2] = "Best Director (Nomin.)"
        strangelove.Oscars[3] = "Best Picture (Nomin.)"
        strangelove.Sequel = nil
        //!-output
    */
}

// This test ensures that the program terminates without crashing.
func Test(t *testing.T) {
    // Some other values (YMMV)
    Display("os.Stderr", os.Stderr)
    // Output:
    // Display os.Stderr (*os.File):
    // (*(*os.Stderr).file).fd = 2
    // (*(*os.Stderr).file).name = "/dev/stderr"
    // (*(*os.Stderr).file).nepipe = 0

    var w io.Writer = os.Stderr
    Display("&w", &w)
    // Output:
    // Display &w (*io.Writer):
    // (*&w).type = *os.File
    // (*(*(*&w).value).file).fd = 2
    // (*(*(*&w).value).file).name = "/dev/stderr"
    // (*(*(*&w).value).file).nepipe = 0

    var locker sync.Locker = new(sync.Mutex)
    Display("(&locker)", &locker)
    // Output:
    // Display (&locker) (*sync.Locker):
    // (*(&locker)).type = *sync.Mutex
    // (*(*(&locker)).value).state = 0
    // (*(*(&locker)).value).sema = 0

    Display("locker", locker)
    // Output:
    // Display locker (*sync.Mutex):
    // (*locker).state = 0
    // (*locker).sema = 0
    // (*(&locker)) = nil

    locker = nil
    Display("(&locker)", &locker)
    // Output:
    // Display (&locker) (*sync.Locker):
    // (*(&locker)) = nil

    ips, _ := net.LookupHost("golang.org")
    Display("ips", ips)
    // Output:
    // Display ips ([]string):
    // ips[0] = "173.194.68.141"
    // ips[1] = "2607:f8b0:400d:c06::8d"

    // Even metarecursion!  (YMMV)
    Display("rV", reflect.ValueOf(os.Stderr))
    // Output:
    // Display rV (reflect.Value):
    // (*rV.typ).size = 8
    // (*rV.typ).ptrdata = 8
    // (*rV.typ).hash = 871609668
    // (*rV.typ)._ = 0
    // ...

    // a pointer that points to itself
    type P *P
    var p P
    p = &p
    if false {
        Display("p", p)
        // Output:
        // Display p (display.P):
        // ...stuck, no output...
    }

    // a map that contains itself
    type M map[string]M
    m := make(M)
    m[""] = m
    if false {
        Display("m", m)
        // Output:
        // Display m (display.M):
        // ...stuck, no output...
    }

    // a slice that contains itself
    type S []S
    s := make(S, 1)
    s[0] = s
    if false {
        Display("s", s)
        // Output:
        // Display s (display.S):
        // ...stuck, no output...
    }

    // a linked list that eats its own tail
    type Cycle struct {
        Value int
        Tail  *Cycle
    }
    var c Cycle
    c = Cycle{42, &c}
    if false {
        Display("c", c)
        // Output:
        // Display c (display.Cycle):
        // c.Value = 42
        // (*c.Tail).Value = 42
        // (*(*c.Tail).Tail).Value = 42
        // ...ad infinitum...
    }
}