1// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// bpdoc docs.
16package bpdoc
17
18import (
19	"html/template"
20	"reflect"
21	"runtime"
22	"testing"
23
24	"github.com/google/blueprint"
25)
26
27type factoryFn func() (blueprint.Module, []interface{})
28
29// foo docs.
30func fooFactory() (blueprint.Module, []interface{}) {
31	return nil, []interface{}{&props{}}
32}
33
34// bar docs.
35func barFactory() (blueprint.Module, []interface{}) {
36	return nil, []interface{}{&complexProps{}}
37}
38
39type structToNest struct {
40	E string
41}
42
43type StructToEmbed struct {
44	Nested_in_embedded structToNest
45
46	// F string
47	F string
48}
49
50type otherStructToNest struct {
51	G string
52}
53
54type OtherStructToEmbed struct {
55	Nested_in_other_embedded otherStructToNest
56
57	// F string
58	H string
59}
60
61type StructWithEmbedded struct {
62	StructToEmbed
63}
64
65// for bpdoc_test.go
66type complexProps struct {
67	A         string
68	B_mutated string `blueprint:"mutated"`
69
70	Nested struct {
71		C         string
72		D_mutated string `blueprint:"mutated"`
73	}
74
75	Nested_struct structToNest
76
77	Struct_has_embed StructWithEmbedded
78
79	OtherStructToEmbed
80
81	List_of_ints []int
82
83	List_of_nested []structToNest
84}
85
86// props docs.
87type props struct {
88	// A docs.
89	A string
90}
91
92// for properties_test.go
93type tagTestProps struct {
94	A string `tag1:"a,b" tag2:"c"`
95	B string `tag1:"a,c"`
96	C string `tag1:"b,c"`
97
98	D struct {
99		E string `tag1:"a,b" tag2:"c"`
100		F string `tag1:"a,c"`
101		G string `tag1:"b,c"`
102	} `tag1:"b,c"`
103}
104
105var pkgPath string
106var pkgFiles map[string][]string
107var moduleTypeNameFactories map[string]reflect.Value
108var moduleTypeNamePropertyStructs map[string][]interface{}
109
110func init() {
111	pc, filename, _, _ := runtime.Caller(0)
112	fn := runtime.FuncForPC(pc)
113
114	var err error
115	pkgPath, err = funcNameToPkgPath(fn.Name())
116	if err != nil {
117		panic(err)
118	}
119
120	pkgFiles = map[string][]string{
121		pkgPath: {filename},
122	}
123
124	factories := map[string]factoryFn{"foo": fooFactory, "bar": barFactory}
125
126	moduleTypeNameFactories = make(map[string]reflect.Value, len(factories))
127	moduleTypeNamePropertyStructs = make(map[string][]interface{}, len(factories))
128	for name, factory := range factories {
129		moduleTypeNameFactories[name] = reflect.ValueOf(factory)
130		_, structs := factory()
131		moduleTypeNamePropertyStructs[name] = structs
132	}
133}
134
135func TestModuleTypeDocs(t *testing.T) {
136	r := NewReader(pkgFiles)
137	for m := range moduleTypeNameFactories {
138		mt, err := r.ModuleType(m+"_module", moduleTypeNameFactories[m])
139		if err != nil {
140			t.Fatal(err)
141		}
142
143		expectedText := template.HTML(m + " docs.\n\n")
144		if mt.Text != expectedText {
145			t.Errorf("unexpected docs %q", mt.Text)
146		}
147
148		if mt.PkgPath != pkgPath {
149			t.Errorf("expected pkgpath %q, got %q", pkgPath, mt.PkgPath)
150		}
151	}
152}
153
154func TestPropertyStruct(t *testing.T) {
155	r := NewReader(pkgFiles)
156	ps, err := r.PropertyStruct(pkgPath, "props", reflect.ValueOf(props{A: "B"}))
157	if err != nil {
158		t.Fatal(err)
159	}
160
161	if ps.Text != "props docs.\n" {
162		t.Errorf("unexpected docs %q", ps.Text)
163	}
164	if len(ps.Properties) != 1 {
165		t.Fatalf("want 1 property, got %d", len(ps.Properties))
166	}
167
168	if ps.Properties[0].Name != "a" || ps.Properties[0].Text != "A docs.\n\n" || ps.Properties[0].Default != "B" {
169		t.Errorf("unexpected property docs %q %q %q",
170			ps.Properties[0].Name, ps.Properties[0].Text, ps.Properties[0].Default)
171	}
172}
173
174func TestPackage(t *testing.T) {
175	r := NewReader(pkgFiles)
176	pkg, err := r.Package(pkgPath)
177	if err != nil {
178		t.Fatal(err)
179	}
180
181	if pkg.Text != "bpdoc docs.\n" {
182		t.Errorf("unexpected docs %q", pkg.Text)
183	}
184}
185
186func TestFuncToPkgPath(t *testing.T) {
187	tests := []struct {
188		f    string
189		want string
190	}{
191		{
192			f:    "github.com/google/blueprint/bootstrap.Main",
193			want: "github.com/google/blueprint/bootstrap",
194		},
195		{
196			f:    "android/soong/android.GenruleFactory",
197			want: "android/soong/android",
198		},
199		{
200			f:    "android/soong/android.ModuleFactoryAdapter.func1",
201			want: "android/soong/android",
202		},
203		{
204			f:    "main.Main",
205			want: "main",
206		},
207	}
208	for _, tt := range tests {
209		t.Run(tt.f, func(t *testing.T) {
210			got, err := funcNameToPkgPath(tt.f)
211			if err != nil {
212				t.Fatal(err)
213			}
214			if got != tt.want {
215				t.Errorf("funcNameToPkgPath(%v) = %v, want %v", tt.f, got, tt.want)
216			}
217		})
218	}
219}
220