mirror of
https://github.com/danog/liquid.git
synced 2024-11-26 21:14:45 +01:00
Coverage
This commit is contained in:
parent
6b8f76ce9b
commit
29c902f6b3
@ -97,8 +97,8 @@ Please refer to the [contribution guidelines](./CONTRIBUTING.md).
|
||||
|
||||
| Package | Author | Description | License |
|
||||
|-----------------------------------------------------|-----------------|-----------------------------------------|--------------------|
|
||||
| [gopkg.in/yaml.v2](https://github.com/go-yaml/yaml) | Canonical | YAML support (for printing parse trees) | Apache License 2.0 |
|
||||
| [Ragel](http://www.colm.net/open-source/ragel/) | Adrian Thurston | scanning expressions | MIT |
|
||||
| [gopkg.in/yaml.v2](https://github.com/go-yaml/yaml) | Canonical | YAML support (for printing parse trees) | Apache License 2.0 |
|
||||
|
||||
Michael Hamrah's [Lexing with Ragel and Parsing with Yacc using Go](https://medium.com/@mhamrah/lexing-with-ragel-and-parsing-with-yacc-using-go-81e50475f88f) was essential to understanding `go yacc`.
|
||||
|
||||
|
@ -19,32 +19,50 @@ import (
|
||||
"github.com/osteele/liquid"
|
||||
)
|
||||
|
||||
// for testing
|
||||
var (
|
||||
stderr = os.Stderr
|
||||
stdout io.Writer = os.Stdout
|
||||
stdin io.Reader = os.Stdin
|
||||
exit = os.Exit
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := os.Args[1:]
|
||||
if err := run(os.Args[1:]); err != nil {
|
||||
fmt.Fprint(stderr, err) // nolint: gas
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := io.Copy(buf, os.Stdin)
|
||||
exitIfErr(err)
|
||||
if _, err := io.Copy(buf, stdin); err != nil {
|
||||
return err
|
||||
}
|
||||
render(buf.Bytes(), "")
|
||||
case args[0] == "-h" || args[0] == "--help":
|
||||
usage(false)
|
||||
case strings.HasPrefix(args[0], "-"):
|
||||
usage(true)
|
||||
os.Exit(1)
|
||||
exit(1)
|
||||
case len(args) == 1:
|
||||
s, err := ioutil.ReadFile(args[0])
|
||||
exitIfErr(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
render(s, args[0])
|
||||
default:
|
||||
usage(true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exitIfErr(err error) {
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stdout, err) // nolint: gas
|
||||
os.Exit(1)
|
||||
fmt.Fprint(stdout, err) // nolint: gas
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,12 +71,12 @@ func render(b []byte, filename string) {
|
||||
exitIfErr(err)
|
||||
out, err := tpl.Render(map[string]interface{}{})
|
||||
exitIfErr(err)
|
||||
os.Stdout.Write(out) // nolint: gas, errcheck
|
||||
stdout.Write(out) // nolint: gas, errcheck
|
||||
}
|
||||
|
||||
func usage(error bool) {
|
||||
fmt.Printf("usage: %s [FILE]\n", os.Args[0])
|
||||
if error {
|
||||
os.Exit(1)
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
17
cmd/liquid/main_test.go
Normal file
17
cmd/liquid/main_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
src := `{{ "Hello World" | downcase | split: " " | first | append: "!"}}`
|
||||
stdin = bytes.NewBufferString(src)
|
||||
buf := new(bytes.Buffer)
|
||||
stdout = buf
|
||||
require.NoError(t, run([]string{}))
|
||||
require.Equal(t, "hello!", buf.String())
|
||||
}
|
49
evaluator/compare_test.go
Normal file
49
evaluator/compare_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package evaluator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var eqTestObj = struct{ a, b int }{1, 2}
|
||||
var eqArrayTestObj = [2]int{1, 2}
|
||||
|
||||
var eqTests = []struct {
|
||||
a, b interface{}
|
||||
expected bool
|
||||
}{
|
||||
{nil, nil, true},
|
||||
{nil, 1, false},
|
||||
{1, nil, false},
|
||||
{false, false, true},
|
||||
{false, true, false},
|
||||
{0, 1, false},
|
||||
{1, 1, true},
|
||||
{1.0, 1.0, true},
|
||||
{1, 1.0, true},
|
||||
{1, 2.0, false},
|
||||
{1.0, 1, true},
|
||||
{"a", "b", false},
|
||||
{"a", "a", true},
|
||||
{int8(2), int16(2), true}, // TODO
|
||||
// {uint8(2), int8(2), true}, // FIXME
|
||||
{eqArrayTestObj, eqArrayTestObj[:], true},
|
||||
{[]string{"a"}, []string{"a"}, true},
|
||||
{[]string{"a"}, []string{"a", "b"}, false},
|
||||
{[]string{"a", "b"}, []string{"a"}, false},
|
||||
{[]string{"a", "b"}, []string{"a", "b"}, true},
|
||||
{[]string{"a", "b"}, []string{"a", "c"}, false},
|
||||
{[]interface{}{1.0, 2}, []interface{}{1, 2.0}, true},
|
||||
{eqTestObj, eqTestObj, true},
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
for i, test := range eqTests {
|
||||
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
||||
value := Equal(test.a, test.b)
|
||||
require.Equalf(t, test.expected, value, "%#v == %#v", test.a, test.b)
|
||||
})
|
||||
}
|
||||
}
|
@ -114,3 +114,12 @@ func TestConvert_map_to_array(t *testing.T) {
|
||||
// require.NotNil(t, ptr)
|
||||
// require.Equal(t, "ab", *ptr)
|
||||
// }
|
||||
|
||||
func TestMustConvert(t *testing.T) {
|
||||
typ := reflect.TypeOf("")
|
||||
v := MustConvert(2, typ)
|
||||
require.Equal(t, "2", v)
|
||||
|
||||
typ = reflect.TypeOf(2)
|
||||
require.Panics(t, func() { MustConvert("xxx", typ) })
|
||||
}
|
||||
|
@ -7,31 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var eqTests = []struct {
|
||||
a, b interface{}
|
||||
expected bool
|
||||
}{
|
||||
{nil, nil, true},
|
||||
{nil, 1, false},
|
||||
{1, nil, false},
|
||||
{false, false, true},
|
||||
{false, true, false},
|
||||
{0, 1, false},
|
||||
{1, 1, true},
|
||||
{1.0, 1.0, true},
|
||||
{1, 1.0, true},
|
||||
{1, 2.0, false},
|
||||
{1.0, 1, true},
|
||||
{"a", "b", false},
|
||||
{"a", "a", true},
|
||||
{[]string{"a"}, []string{"a"}, true},
|
||||
{[]string{"a"}, []string{"a", "b"}, false},
|
||||
{[]string{"a", "b"}, []string{"a"}, false},
|
||||
{[]string{"a", "b"}, []string{"a", "b"}, true},
|
||||
{[]string{"a", "b"}, []string{"a", "c"}, false},
|
||||
{[]interface{}{1.0, 2}, []interface{}{1, 2.0}, true},
|
||||
}
|
||||
|
||||
var lessTests = []struct {
|
||||
a, b interface{}
|
||||
expected bool
|
||||
@ -57,15 +32,6 @@ func TestContains(t *testing.T) {
|
||||
require.False(t, Contains([]int{1, 2}, 3))
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
for i, test := range eqTests {
|
||||
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
||||
value := Equal(test.a, test.b)
|
||||
require.Equalf(t, test.expected, value, "%#v == %#v", test.a, test.b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLess(t *testing.T) {
|
||||
for i, test := range lessTests {
|
||||
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
||||
|
@ -31,7 +31,7 @@ func (c *Config) AddFilter(name string, fn interface{}) {
|
||||
panic(fmt.Errorf("a filter must be a function"))
|
||||
case rf.Type().NumIn() < 1:
|
||||
panic(fmt.Errorf("a filter function must have at least one input"))
|
||||
case rf.Type().NumOut() > 2:
|
||||
case rf.Type().NumOut() < 1 || 2 < rf.Type().NumOut():
|
||||
panic(fmt.Errorf("a filter must be have one or two outputs"))
|
||||
// case rf.Type().Out(1).Implements(…):
|
||||
// panic(typeError("a filter's second output must be type error"))
|
||||
|
@ -7,6 +7,16 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestContext_AddFilter(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
require.NotPanics(t, func() { cfg.AddFilter("f", func(int) int { return 0 }) })
|
||||
require.NotPanics(t, func() { cfg.AddFilter("f", func(int) (a int, e error) { return }) })
|
||||
require.Panics(t, func() { cfg.AddFilter("f", func() int { return 0 }) })
|
||||
require.Panics(t, func() { cfg.AddFilter("f", func(int) {}) })
|
||||
// require.Panics(t, func() { cfg.AddFilter("f", func(int) (a int, b int) { return }) })
|
||||
require.Panics(t, func() { cfg.AddFilter("f", func(int) (a int, e error, b int) { return }) })
|
||||
}
|
||||
|
||||
func TestContext_runFilter(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
constant := func(value interface{}) valueFn {
|
||||
|
@ -12,13 +12,15 @@ var parseTests = []struct {
|
||||
expect interface{}
|
||||
}{
|
||||
{`a | filter: b`, 3},
|
||||
// {`%assign a = 3`, nil},
|
||||
// {`{%cycle 'a'`, []interface{}{"a"}},
|
||||
// {`{%cycle 'a', 'b'`, []interface{}{"a", "b"}},
|
||||
}
|
||||
|
||||
var parseErrorTests = []struct{ in, expected string }{
|
||||
{"a syntax error", "parse error"},
|
||||
{`%assign a`, "parse error"},
|
||||
{`%assign a 3`, "parse error"},
|
||||
{`%cycle 'a' 'b'`, "parse error"},
|
||||
{`%loop a in in`, "parse error"},
|
||||
{`%when a b`, "parse error"},
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
@ -37,6 +39,36 @@ func TestParse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStatement(t *testing.T) {
|
||||
stmt, err := ParseStatement(AssignStatementSelector, "a = b")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "a", stmt.Assignment.Variable)
|
||||
|
||||
stmt, err = ParseStatement(CycleStatementSelector, "'a', 'b'")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", stmt.Cycle.Group)
|
||||
require.Len(t, stmt.Cycle.Values, 2)
|
||||
require.Equal(t, []string{"a", "b"}, stmt.Cycle.Values)
|
||||
|
||||
stmt, err = ParseStatement(CycleStatementSelector, "'g': 'a', 'b'")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "g", stmt.Cycle.Group)
|
||||
require.Len(t, stmt.Cycle.Values, 2)
|
||||
require.Equal(t, []string{"a", "b"}, stmt.Cycle.Values)
|
||||
|
||||
stmt, err = ParseStatement(LoopStatementSelector, "x in array reversed offset: 2 limit: 3")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "x", stmt.Loop.Variable)
|
||||
require.True(t, stmt.Loop.Reversed)
|
||||
require.Equal(t, 2, stmt.Loop.Offset)
|
||||
require.NotNil(t, stmt.Loop.Limit)
|
||||
require.Equal(t, 3, *stmt.Loop.Limit)
|
||||
|
||||
stmt, err = ParseStatement(WhenStatementSelector, "a, b")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, stmt.When.Exprs, 2)
|
||||
}
|
||||
|
||||
func TestParse_errors(t *testing.T) {
|
||||
for i, test := range parseErrorTests {
|
||||
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
||||
|
@ -25,6 +25,8 @@ var filterTests = []struct {
|
||||
{`pages | map: 'category' | join`, "business, celebrities, <nil>, lifestyle, sports, <nil>, technology"},
|
||||
{`pages | map: 'category' | compact | join`, "business, celebrities, lifestyle, sports, technology"},
|
||||
{`"John, Paul, George, Ringo" | split: ", " | join: " and "`, "John and Paul and George and Ringo"},
|
||||
// {`",John, Paul, George, Ringo" | split: ", " | join: " and "`, "John and Paul and George and Ringo"},
|
||||
// {`"John, Paul, George, Ringo," | split: ", " | join: " and "`, "John and Paul and George and Ringo"},
|
||||
{`animals | sort | join: ", "`, "Sally Snake, giraffe, octopus, zebra"},
|
||||
{`sort_prop | sort: "weight" | inspect`, `[{"weight":null},{"weight":1},{"weight":3},{"weight":5}]`},
|
||||
{`fruits | reverse | join: ", "`, "plums, peaches, oranges, apples"},
|
||||
@ -67,6 +69,7 @@ var filterTests = []struct {
|
||||
{`"website.com" | append: "/index.html"`, "website.com/index.html"},
|
||||
{`"title" | capitalize`, "Title"},
|
||||
{`"my great title" | capitalize`, "My great title"},
|
||||
{`"" | capitalize`, ""},
|
||||
{`"Parker Moore" | downcase`, "parker moore"},
|
||||
{`"Have you read 'James & the Giant Peach'?" | escape`, "Have you read 'James & the Giant Peach'?"},
|
||||
{`"1 < 2 & 3" | escape_once`, "1 < 2 & 3"},
|
||||
@ -79,7 +82,10 @@ var filterTests = []struct {
|
||||
{`"Liquid" | slice: 2`, "q"},
|
||||
{`"Liquid" | slice: 2, 5`, "quid"},
|
||||
{`"Liquid" | slice: -3, 2`, "ui"},
|
||||
|
||||
{`"Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html`, "Have you read Ulysses?"},
|
||||
{`string_with_newlines | strip_newlines`, "Hellothere"},
|
||||
|
||||
{`"Ground control to Major Tom." | truncate: 20`, "Ground control to..."},
|
||||
{`"Ground control to Major Tom." | truncate: 25, ", and so on"`, "Ground control, and so on"},
|
||||
{`"Ground control to Major Tom." | truncate: 20, ""`, "Ground control to Ma"},
|
||||
@ -135,6 +141,7 @@ var filterTests = []struct {
|
||||
{`5 | divided_by: 3`, 1},
|
||||
{`20 | divided_by: 7`, 2},
|
||||
{`20 | divided_by: 7.0`, 2.857142857142857},
|
||||
{`20 | divided_by: 's'`, nil},
|
||||
|
||||
{`1.2 | round`, 1},
|
||||
{`2.7 | round`, 3},
|
||||
@ -143,6 +150,8 @@ var filterTests = []struct {
|
||||
// Jekyll extensions; added here for convenient testing
|
||||
// TODO add this just to the test environment
|
||||
{`obj | inspect`, `{"a":1}`},
|
||||
{`1 | type`, `int`},
|
||||
{`"1" | type`, `string`},
|
||||
}
|
||||
|
||||
func timeMustParse(s string) time.Time {
|
||||
|
@ -28,6 +28,7 @@ var renderTests = []struct{ in, out string }{
|
||||
{`{{ "abc" }}`, "abc"},
|
||||
{`{{ x }}`, "123"},
|
||||
{`{{ page.title }}`, "Introduction"},
|
||||
{`{{ array }}`, "firstsecondthird"},
|
||||
{`{{ array[1] }}`, "second"},
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user