| // 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 |
| } |