1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2019 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage bpdoc 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "reflect" 19*1fa6dee9SAndroid Build Coastguard Worker "strings" 20*1fa6dee9SAndroid Build Coastguard Worker "testing" 21*1fa6dee9SAndroid Build Coastguard Worker) 22*1fa6dee9SAndroid Build Coastguard Worker 23*1fa6dee9SAndroid Build Coastguard Workerfunc TestExcludeByTag(t *testing.T) { 24*1fa6dee9SAndroid Build Coastguard Worker r := NewReader(pkgFiles) 25*1fa6dee9SAndroid Build Coastguard Worker ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{})) 26*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 27*1fa6dee9SAndroid Build Coastguard Worker t.Fatal(err) 28*1fa6dee9SAndroid Build Coastguard Worker } 29*1fa6dee9SAndroid Build Coastguard Worker 30*1fa6dee9SAndroid Build Coastguard Worker ps.ExcludeByTag("tag1", "a") 31*1fa6dee9SAndroid Build Coastguard Worker 32*1fa6dee9SAndroid Build Coastguard Worker expected := []string{"c", "d", "g"} 33*1fa6dee9SAndroid Build Coastguard Worker actual := actualProperties(t, ps.Properties) 34*1fa6dee9SAndroid Build Coastguard Worker if !reflect.DeepEqual(expected, actual) { 35*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("unexpected ExcludeByTag result, expected: %q, actual: %q", expected, actual) 36*1fa6dee9SAndroid Build Coastguard Worker } 37*1fa6dee9SAndroid Build Coastguard Worker} 38*1fa6dee9SAndroid Build Coastguard Worker 39*1fa6dee9SAndroid Build Coastguard Workerfunc TestIncludeByTag(t *testing.T) { 40*1fa6dee9SAndroid Build Coastguard Worker r := NewReader(pkgFiles) 41*1fa6dee9SAndroid Build Coastguard Worker ps, err := r.PropertyStruct(pkgPath, "tagTestProps", reflect.ValueOf(tagTestProps{A: "B"})) 42*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 43*1fa6dee9SAndroid Build Coastguard Worker t.Fatal(err) 44*1fa6dee9SAndroid Build Coastguard Worker } 45*1fa6dee9SAndroid Build Coastguard Worker 46*1fa6dee9SAndroid Build Coastguard Worker ps.IncludeByTag("tag1", "c") 47*1fa6dee9SAndroid Build Coastguard Worker 48*1fa6dee9SAndroid Build Coastguard Worker expected := []string{"b", "c", "d", "f", "g"} 49*1fa6dee9SAndroid Build Coastguard Worker actual := actualProperties(t, ps.Properties) 50*1fa6dee9SAndroid Build Coastguard Worker if !reflect.DeepEqual(expected, actual) { 51*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("unexpected IncludeByTag result, expected: %q, actual: %q", expected, actual) 52*1fa6dee9SAndroid Build Coastguard Worker } 53*1fa6dee9SAndroid Build Coastguard Worker} 54*1fa6dee9SAndroid Build Coastguard Worker 55*1fa6dee9SAndroid Build Coastguard Workerfunc TestPropertiesOfReflectionStructs(t *testing.T) { 56*1fa6dee9SAndroid Build Coastguard Worker testCases := []struct { 57*1fa6dee9SAndroid Build Coastguard Worker fields map[string]interface{} 58*1fa6dee9SAndroid Build Coastguard Worker expectedProperties map[string]Property 59*1fa6dee9SAndroid Build Coastguard Worker description string 60*1fa6dee9SAndroid Build Coastguard Worker }{ 61*1fa6dee9SAndroid Build Coastguard Worker { 62*1fa6dee9SAndroid Build Coastguard Worker fields: map[string]interface{}{ 63*1fa6dee9SAndroid Build Coastguard Worker "A": "A is a string", 64*1fa6dee9SAndroid Build Coastguard Worker "B": 0, //B is an int 65*1fa6dee9SAndroid Build Coastguard Worker }, 66*1fa6dee9SAndroid Build Coastguard Worker expectedProperties: map[string]Property{ 67*1fa6dee9SAndroid Build Coastguard Worker "a": *createProperty("a", "string", ""), 68*1fa6dee9SAndroid Build Coastguard Worker "b": *createProperty("b", "int", ""), 69*1fa6dee9SAndroid Build Coastguard Worker }, 70*1fa6dee9SAndroid Build Coastguard Worker description: "struct is composed of primitive types", 71*1fa6dee9SAndroid Build Coastguard Worker }, 72*1fa6dee9SAndroid Build Coastguard Worker { 73*1fa6dee9SAndroid Build Coastguard Worker fields: map[string]interface{}{ 74*1fa6dee9SAndroid Build Coastguard Worker "A": "A is a string", 75*1fa6dee9SAndroid Build Coastguard Worker "B": 0, //B is an int 76*1fa6dee9SAndroid Build Coastguard Worker "C": props{}, 77*1fa6dee9SAndroid Build Coastguard Worker }, 78*1fa6dee9SAndroid Build Coastguard Worker expectedProperties: map[string]Property{ 79*1fa6dee9SAndroid Build Coastguard Worker "a": *createProperty("a", "string", ""), 80*1fa6dee9SAndroid Build Coastguard Worker "b": *createProperty("b", "int", ""), 81*1fa6dee9SAndroid Build Coastguard Worker "c": *createProperty("c", "props", "props docs."), 82*1fa6dee9SAndroid Build Coastguard Worker }, 83*1fa6dee9SAndroid Build Coastguard Worker description: "struct is composed of primitive types and other structs", 84*1fa6dee9SAndroid Build Coastguard Worker }, 85*1fa6dee9SAndroid Build Coastguard Worker } 86*1fa6dee9SAndroid Build Coastguard Worker 87*1fa6dee9SAndroid Build Coastguard Worker r := NewReader(pkgFiles) 88*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range testCases { 89*1fa6dee9SAndroid Build Coastguard Worker structType := reflectionStructType(testCase.fields) 90*1fa6dee9SAndroid Build Coastguard Worker ps, err := r.PropertyStruct(structType.PkgPath(), structType.String(), reflect.New(structType).Elem()) 91*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 92*1fa6dee9SAndroid Build Coastguard Worker t.Fatal(err) 93*1fa6dee9SAndroid Build Coastguard Worker } 94*1fa6dee9SAndroid Build Coastguard Worker for _, actualProperty := range ps.Properties { 95*1fa6dee9SAndroid Build Coastguard Worker propName := actualProperty.Name 96*1fa6dee9SAndroid Build Coastguard Worker assertProperties(t, testCase.expectedProperties[propName], actualProperty) 97*1fa6dee9SAndroid Build Coastguard Worker } 98*1fa6dee9SAndroid Build Coastguard Worker } 99*1fa6dee9SAndroid Build Coastguard Worker} 100*1fa6dee9SAndroid Build Coastguard Worker 101*1fa6dee9SAndroid Build Coastguard Workerfunc TestNestUnique(t *testing.T) { 102*1fa6dee9SAndroid Build Coastguard Worker testCases := []struct { 103*1fa6dee9SAndroid Build Coastguard Worker src []Property 104*1fa6dee9SAndroid Build Coastguard Worker target []Property 105*1fa6dee9SAndroid Build Coastguard Worker expected []Property 106*1fa6dee9SAndroid Build Coastguard Worker description string 107*1fa6dee9SAndroid Build Coastguard Worker }{ 108*1fa6dee9SAndroid Build Coastguard Worker { 109*1fa6dee9SAndroid Build Coastguard Worker src: []Property{}, 110*1fa6dee9SAndroid Build Coastguard Worker target: []Property{}, 111*1fa6dee9SAndroid Build Coastguard Worker expected: []Property{}, 112*1fa6dee9SAndroid Build Coastguard Worker description: "Nest Unique fails for empty slice", 113*1fa6dee9SAndroid Build Coastguard Worker }, 114*1fa6dee9SAndroid Build Coastguard Worker { 115*1fa6dee9SAndroid Build Coastguard Worker src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, 116*1fa6dee9SAndroid Build Coastguard Worker target: []Property{}, 117*1fa6dee9SAndroid Build Coastguard Worker expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, 118*1fa6dee9SAndroid Build Coastguard Worker description: "Nest Unique fails when all elements are unique", 119*1fa6dee9SAndroid Build Coastguard Worker }, 120*1fa6dee9SAndroid Build Coastguard Worker { 121*1fa6dee9SAndroid Build Coastguard Worker src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, 122*1fa6dee9SAndroid Build Coastguard Worker target: []Property{*createProperty("c", "string", "")}, 123*1fa6dee9SAndroid Build Coastguard Worker expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", ""), *createProperty("c", "string", "")}, 124*1fa6dee9SAndroid Build Coastguard Worker description: "Nest Unique fails when all elements are unique", 125*1fa6dee9SAndroid Build Coastguard Worker }, 126*1fa6dee9SAndroid Build Coastguard Worker { 127*1fa6dee9SAndroid Build Coastguard Worker src: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, 128*1fa6dee9SAndroid Build Coastguard Worker target: []Property{*createProperty("a", "string", "")}, 129*1fa6dee9SAndroid Build Coastguard Worker expected: []Property{*createProperty("a", "string", ""), *createProperty("b", "string", "")}, 130*1fa6dee9SAndroid Build Coastguard Worker description: "Nest Unique fails when nested elements are duplicate", 131*1fa6dee9SAndroid Build Coastguard Worker }, 132*1fa6dee9SAndroid Build Coastguard Worker } 133*1fa6dee9SAndroid Build Coastguard Worker 134*1fa6dee9SAndroid Build Coastguard Worker errMsgTemplate := "%s. Expected: %q, Actual: %q" 135*1fa6dee9SAndroid Build Coastguard Worker for _, testCase := range testCases { 136*1fa6dee9SAndroid Build Coastguard Worker actual := nestUnique(testCase.src, testCase.target) 137*1fa6dee9SAndroid Build Coastguard Worker if len(actual) != len(testCase.expected) { 138*1fa6dee9SAndroid Build Coastguard Worker t.Errorf(errMsgTemplate, testCase.description, testCase.expected, actual) 139*1fa6dee9SAndroid Build Coastguard Worker } 140*1fa6dee9SAndroid Build Coastguard Worker for i := 0; i < len(actual); i++ { 141*1fa6dee9SAndroid Build Coastguard Worker if !actual[i].Equal(testCase.expected[i]) { 142*1fa6dee9SAndroid Build Coastguard Worker t.Errorf(errMsgTemplate, testCase.description, testCase.expected[i], actual[i]) 143*1fa6dee9SAndroid Build Coastguard Worker } 144*1fa6dee9SAndroid Build Coastguard Worker } 145*1fa6dee9SAndroid Build Coastguard Worker } 146*1fa6dee9SAndroid Build Coastguard Worker} 147*1fa6dee9SAndroid Build Coastguard Worker 148*1fa6dee9SAndroid Build Coastguard Worker// Creates a struct using reflection and return its type 149*1fa6dee9SAndroid Build Coastguard Workerfunc reflectionStructType(fields map[string]interface{}) reflect.Type { 150*1fa6dee9SAndroid Build Coastguard Worker var structFields []reflect.StructField 151*1fa6dee9SAndroid Build Coastguard Worker for fieldname, obj := range fields { 152*1fa6dee9SAndroid Build Coastguard Worker structField := reflect.StructField{ 153*1fa6dee9SAndroid Build Coastguard Worker Name: fieldname, 154*1fa6dee9SAndroid Build Coastguard Worker Type: reflect.TypeOf(obj), 155*1fa6dee9SAndroid Build Coastguard Worker } 156*1fa6dee9SAndroid Build Coastguard Worker structFields = append(structFields, structField) 157*1fa6dee9SAndroid Build Coastguard Worker } 158*1fa6dee9SAndroid Build Coastguard Worker return reflect.StructOf(structFields) 159*1fa6dee9SAndroid Build Coastguard Worker} 160*1fa6dee9SAndroid Build Coastguard Worker 161*1fa6dee9SAndroid Build Coastguard Worker// Creates a Property object with a subset of its props populated 162*1fa6dee9SAndroid Build Coastguard Workerfunc createProperty(propName string, propType string, propDocs string) *Property { 163*1fa6dee9SAndroid Build Coastguard Worker return &Property{Name: propName, Type: propType, Text: formatText(propDocs)} 164*1fa6dee9SAndroid Build Coastguard Worker} 165*1fa6dee9SAndroid Build Coastguard Worker 166*1fa6dee9SAndroid Build Coastguard Worker// Asserts that two Property objects are "similar" 167*1fa6dee9SAndroid Build Coastguard Worker// Name, Type and Text properties are checked for similarity 168*1fa6dee9SAndroid Build Coastguard Workerfunc assertProperties(t *testing.T, expected Property, actual Property) { 169*1fa6dee9SAndroid Build Coastguard Worker assertStrings(t, expected.Name, actual.Name) 170*1fa6dee9SAndroid Build Coastguard Worker assertStrings(t, expected.Type, actual.Type) 171*1fa6dee9SAndroid Build Coastguard Worker assertStrings(t, strings.TrimSpace(string(expected.Text)), strings.TrimSpace(string(actual.Text))) 172*1fa6dee9SAndroid Build Coastguard Worker} 173*1fa6dee9SAndroid Build Coastguard Worker 174*1fa6dee9SAndroid Build Coastguard Workerfunc assertStrings(t *testing.T, expected string, actual string) { 175*1fa6dee9SAndroid Build Coastguard Worker if expected != actual { 176*1fa6dee9SAndroid Build Coastguard Worker t.Errorf("expected: %s, actual: %s", expected, actual) 177*1fa6dee9SAndroid Build Coastguard Worker } 178*1fa6dee9SAndroid Build Coastguard Worker} 179*1fa6dee9SAndroid Build Coastguard Worker 180*1fa6dee9SAndroid Build Coastguard Workerfunc actualProperties(t *testing.T, props []Property) []string { 181*1fa6dee9SAndroid Build Coastguard Worker t.Helper() 182*1fa6dee9SAndroid Build Coastguard Worker 183*1fa6dee9SAndroid Build Coastguard Worker actual := []string{} 184*1fa6dee9SAndroid Build Coastguard Worker for _, p := range props { 185*1fa6dee9SAndroid Build Coastguard Worker actual = append(actual, p.Name) 186*1fa6dee9SAndroid Build Coastguard Worker actual = append(actual, actualProperties(t, p.Properties)...) 187*1fa6dee9SAndroid Build Coastguard Worker } 188*1fa6dee9SAndroid Build Coastguard Worker return actual 189*1fa6dee9SAndroid Build Coastguard Worker} 190