blob: b0b3ae447a2416790135f4c6d46049731f4dd267 [file] [log] [blame]
// Copyright 2019 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bpdoc
import (
"reflect"
"strings"
"testing"
)
func TestExcludeByTag(t *testing.T) {
r := NewReader(pkgFiles)
ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{}))
if err != nil {
t.Fatal(err)
}
ps.ExcludeByTag("tag1", "a")
expected := []string{"c", "d", "g"}
actual := actualProperties(t, ps.Properties)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("unexpected ExcludeByTag result, expected: %q, actual: %q", expected, actual)
}
}
func TestIncludeByTag(t *testing.T) {
r := NewReader(pkgFiles)
ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{A: "B"}))
if err != nil {
t.Fatal(err)
}
ps.IncludeByTag("tag1", "c")
expected := []string{"b", "c", "d", "f", "g"}
actual := actualProperties(t, ps.Properties)
if !reflect.DeepEqual(expected, actual) {
t.Errorf("unexpected IncludeByTag result, expected: %q, actual: %q", expected, actual)
}
}
func TestPropertiesOfReflectionStructs(t *testing.T) {
testCases := []struct {
fields map[string]interface{}
expectedProperties map[string]Property
description string
}{
{
fields: map[string]interface{}{
"A": "A is a string",
"B": 0, //B is an int
},
expectedProperties: map[string]Property{
"a": *createProperty("a", "string", ""),
"b": *createProperty("b", "int", ""),
},
description: "struct is composed of primitive types",
},
{
fields: map[string]interface{}{
"A": "A is a string",
"B": 0, //B is an int
"C": props{},
},
expectedProperties: map[string]Property{
"a": *createProperty("a", "string", ""),
"b": *createProperty("b", "int", ""),
"c": *createProperty("c", "props", "props docs."),
},
description: "struct is composed of primitive types and other structs",
},
}
r := NewReader(pkgFiles)
for _, testCase := range testCases {
structType := reflectionStructType(testCase.fields)
ps, err := r.PropertyStruct(structType.PkgPath(), structType.String(), reflect.New(structType).Elem())
if err != nil {
t.Fatal(err)
}
for _, actualProperty := range ps.Properties {
propName := actualProperty.Name
assertProperties(t, testCase.expectedProperties[propName], actualProperty)
}
}
}
func TestNestUnique(t *testing.T) {
testCases := []struct {
src []Property
target []Property
expected []Property
description string
}{
{
src: []Property{},
target: []Property{},
expected: []Property{},
description: "Nest Unique fails for empty slice",
},
{
src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
target: []Property{},
expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
description: "Nest Unique fails when all elements are unique",
},
{
src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
target: []Property{*createProperty("c", "string", "")},
expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", ""), *createProperty("c", "string", "")},
description: "Nest Unique fails when all elements are unique",
},
{
src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
target: []Property{*createProperty("a", "string", "")},
expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")},
description: "Nest Unique fails when nested elements are duplicate",
},
}
errMsgTemplate := "%s. Expected: %q, Actual: %q"
for _, testCase := range testCases {
actual := nestUnique(testCase.src, testCase.target)
if len(actual) != len(testCase.expected) {
t.Errorf(errMsgTemplate, testCase.description, testCase.expected, actual)
}
for i := 0; i < len(actual); i++ {
if !actual[i].Equal(testCase.expected[i]) {
t.Errorf(errMsgTemplate, testCase.description, testCase.expected[i], actual[i])
}
}
}
}
// Creates a struct using reflection and return its type
func reflectionStructType(fields map[string]interface{}) reflect.Type {
var structFields []reflect.StructField
for fieldname, obj := range fields {
structField := reflect.StructField{
Name: fieldname,
Type: reflect.TypeOf(obj),
}
structFields = append(structFields, structField)
}
return reflect.StructOf(structFields)
}
// Creates a Property object with a subset of its props populated
func createProperty(propName string, propType string, propDocs string) *Property {
return &Property{Name: propName, Type: propType, Text: formatText(propDocs)}
}
// Asserts that two Property objects are "similar"
// Name, Type and Text properties are checked for similarity
func assertProperties(t *testing.T, expected Property, actual Property) {
assertStrings(t, expected.Name, actual.Name)
assertStrings(t, expected.Type, actual.Type)
assertStrings(t, strings.TrimSpace(string(expected.Text)), strings.TrimSpace(string(actual.Text)))
}
func assertStrings(t *testing.T, expected string, actual string) {
if expected != actual {
t.Errorf("expected: %s, actual: %s", expected, actual)
}
}
func actualProperties(t *testing.T, props []Property) []string {
t.Helper()
actual := []string{}
for _, p := range props {
actual = append(actual, p.Name)
actual = append(actual, actualProperties(t, p.Properties)...)
}
return actual
}