alexkappa/mustache

View on GitHub
spec_test.go

Summary

Maintainability
A
35 mins
Test Coverage
// Copyright (c) 2014 Alex Kalyvitis

package mustache

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "runtime/debug"
    "strings"
    "testing"
    "text/tabwriter"
)

const specDir = "./spec/specs"

type Spec struct {
    Overview string `json:"overview"`
    Tests    []struct {
        Name     string            `json:"name"`
        Data     interface{}       `json:"data"`
        Expected string            `json:"expected"`
        Template string            `json:"template"`
        Partials map[string]string `json:"partials"`
        Desc     string            `json:"desc"`
    } `json:"tests"`
}

var specs = make(map[string]Spec)

func init() {
    files, err := ioutil.ReadDir(specDir)
    if err != nil {
        log.Fatal(err)
    }
    for _, file := range files {
        if strings.Contains(file.Name(), ".json") {
            var spec Spec
            f, err := os.Open(filepath.Join(specDir, file.Name()))
            if err != nil {
                log.Fatal(err)
            }
            err = json.NewDecoder(f).Decode(&spec)
            if err != nil {
                log.Fatal(err)
            }
            specs[strings.TrimSuffix(file.Name(), ".json")] = spec
        }
    }
}

type testWriter struct {
    b *bytes.Buffer
    w *tabwriter.Writer
}

func newTestWriter() *testWriter {
    t := &testWriter{}
    t.b = bytes.NewBuffer(nil)
    t.w = tabwriter.NewWriter(t.b, 0, 8, 0, ':', 0)
    return t
}

func (t *testWriter) Write(buf []byte) (n int, err error) { return t.w.Write(buf) }
func (t *testWriter) String() string                      { t.w.Flush(); return t.b.String() }
func (t *testWriter) Reset()                              { t.w.Flush(); t.b.Reset() }

func testSpecFunc(t *testing.T, s Spec) func(t *testing.T) {
    return func(t *testing.T) {

        t.Parallel() // run each spec test concurrently.

        // Create a new writer to write information about the test, and in
        // the event the test fails we print it to the screen.
        w := newTestWriter()

        for _, test := range s.Tests {

            fmt.Fprintf(w, "%s\n", test.Desc)
            fmt.Fprintf(w, "Name: %q\n", test.Name)
            fmt.Fprintf(w, "Template: %q\n", test.Template)
            fmt.Fprintf(w, "Data: %q\n", test.Data)

            // Handle and recover from panics.
            defer func() {
                if r := recover(); r != nil {
                    fmt.Fprintf(w, "Error: %v\n", r)
                    fmt.Fprintf(w, "Stack: %s\n", debug.Stack())
                    t.Fatal(w.String())
                }
            }()

            defer w.Reset() // Reset writer after each test.

            // Parse the template and report errors.
            template := New()
            if err := template.ParseString(test.Template); err != nil {
                fmt.Fprintf(w, "Error: %s\n", err)
                t.Fatal(w.String())
            }

            // If partials were present in the spec test iterate and test each
            // one.
            for n, s := range test.Partials {
                fmt.Fprintf(w, "Partial : %s> %q\n", n, s)
                p := New(Name(n))
                if err := p.ParseString(s); err != nil {
                    fmt.Fprintf(w, "Error: %s\n", err)
                    t.Fatal(w.String())
                }
                template.Option(Partial(p))
            }

            output, err := template.RenderString(test.Data)
            if err != nil {
                fmt.Fprintf(w, "Error: %s\n", err)
                t.Fatal(w.String())
            }

            fmt.Fprintf(w, "Tree: %+v\n", template.elems)
            fmt.Fprintf(w, "Expected: %q\n", test.Expected)
            fmt.Fprintf(w, "Have: %q\n", output)

            if output != test.Expected {
                t.Fatal(w.String())
            }
        }
    }
}

func TestSpec(t *testing.T) {
    t.Run("Comments", testSpecFunc(t, specs["comments"]))
    t.Run("Delimiters", testSpecFunc(t, specs["delimiters"]))
    t.Run("Interpolation", testSpecFunc(t, specs["interpolation"]))
    t.Run("Inverted", testSpecFunc(t, specs["inverted"]))
    t.Run("Partials", func(t *testing.T) {
        t.Skip("skip partials as they don't conform fully to the standard")
        testSpecFunc(t, specs["partials"])(t)
    })
    t.Run("Sections", testSpecFunc(t, specs["sections"]))
}