1c1df7f953
- strip # comments with quote-state tracking so a hash in a string no longer truncates the line - skip heredoc bodies in every @(EOT) form so documentation text is not indexed as a class reference - take every literal name out of a selector, following a bare variable through the assignments in its own file - record a class reference that stays unresolved against its file, with any literal prefix, instead of dropping it - read a class name case insensitively in a title or type reference - tag each edge as a declaration or an ordering reference
145 lines
3.6 KiB
Go
145 lines
3.6 KiB
Go
package impact
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
)
|
|
|
|
// refs runs a manifest body through the same stripping addManifest does, then
|
|
// extracts the class names it resolves.
|
|
func refs(body string) []string {
|
|
names, _ := scanReferences(stripSource(body))
|
|
out := make([]string, 0, len(names))
|
|
for name := range names {
|
|
out = append(out, name)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// unresolvedRefs returns the references the scan could not reduce to names.
|
|
func unresolvedRefs(body string) []UnresolvedRef {
|
|
_, out := scanReferences(stripSource(body))
|
|
return out
|
|
}
|
|
|
|
func TestReferencesReadsWholeArgumentList(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
body string
|
|
want []string
|
|
}{
|
|
{
|
|
name: "bare list",
|
|
body: "class p {\n include foo, bar, baz\n}\n",
|
|
want: []string{"bar", "baz", "foo"},
|
|
},
|
|
{
|
|
name: "top scope names",
|
|
body: "class p {\n include ::foo::bar, ::baz\n}\n",
|
|
want: []string{"baz", "foo::bar"},
|
|
},
|
|
{
|
|
name: "require function",
|
|
body: "class p {\n require foo, bar\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "contain",
|
|
body: "class p {\n contain foo, bar\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "parenthesised",
|
|
body: "class p {\n include(foo, bar)\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "quoted names",
|
|
body: "class p {\n include 'foo::bar', \"baz::qux\"\n}\n",
|
|
want: []string{"baz::qux", "foo::bar"},
|
|
},
|
|
{
|
|
name: "multi line with trailing comma",
|
|
body: "class p {\n include(\n foo::a,\n foo::b,\n )\n}\n",
|
|
want: []string{"foo::a", "foo::b"},
|
|
},
|
|
{
|
|
name: "multi line statement form",
|
|
body: "class p {\n include foo::a,\n foo::b\n}\n",
|
|
want: []string{"foo::a", "foo::b"},
|
|
},
|
|
{
|
|
name: "array argument",
|
|
body: "class p {\n include [foo, bar]\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "class resource title list",
|
|
body: "class p {\n class { ['foo', 'bar']: }\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "class reference title list",
|
|
body: "class p {\n Class['foo'] -> Class['bar', 'baz']\n}\n",
|
|
want: []string{"bar", "baz", "foo"},
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := refs(tc.body); !reflect.DeepEqual(got, tc.want) {
|
|
t.Errorf("references = %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReferencesStopsAtStatementEnd(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
body string
|
|
want []string
|
|
}{
|
|
{
|
|
name: "next statement is not swallowed",
|
|
body: "class p {\n include foo\n notify { 'x': }\n include bar\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "trailing comment is not a name",
|
|
body: "class p {\n include foo, bar # baz, qux\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "block close ends the list",
|
|
body: "class p {\n if $x { include foo }\n include bar\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "require attribute is not a call",
|
|
body: "class p {\n service { 'x':\n require => Class['foo'],\n }\n include bar\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
{
|
|
name: "semicolon ends the list",
|
|
body: "class p {\n include foo; include bar\n}\n",
|
|
want: []string{"bar", "foo"},
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := refs(tc.body); !reflect.DeepEqual(got, tc.want) {
|
|
t.Errorf("references = %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReferencesInheritsTakesOneName(t *testing.T) {
|
|
// inherits is not a function and never takes a list.
|
|
if got, want := refs("class p inherits foo::bar {\n}\n"), []string{"foo::bar"}; !reflect.DeepEqual(got, want) {
|
|
t.Errorf("references = %v, want %v", got, want)
|
|
}
|
|
}
|