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	"github.com/google/blueprint/proptools"
26)
27
28type factoryFn func() (blueprint.Module, []interface{})
29
30// foo docs.
31func fooFactory() (blueprint.Module, []interface{}) {
32	return nil, []interface{}{&props{}}
33}
34
35// bar docs.
36func barFactory() (blueprint.Module, []interface{}) {
37	return nil, []interface{}{&complexProps{}}
38}
39
40type structToNest struct {
41	E string
42}
43
44type StructToEmbed struct {
45	Nested_in_embedded structToNest
46
47	// F string
48	F string
49}
50
51type otherStructToNest struct {
52	G string
53}
54
55type OtherStructToEmbed struct {
56	Nested_in_other_embedded otherStructToNest
57
58	// F string
59	H string
60}
61
62type StructWithEmbedded struct {
63	StructToEmbed
64}
65
66// for bpdoc_test.go
67type complexProps struct {
68	A         string
69	B_mutated string `blueprint:"mutated"`
70
71	Nested struct {
72		C         string
73		D_mutated string `blueprint:"mutated"`
74	}
75
76	Nested_struct structToNest
77
78	Struct_has_embed StructWithEmbedded
79
80	OtherStructToEmbed
81
82	List_of_ints []int
83
84	List_of_nested []structToNest
85
86	Configurable_bool proptools.Configurable[bool]
87}
88
89// props docs.
90type props struct {
91	// A docs.
92	A string
93}
94
95// for properties_test.go
96type tagTestProps struct {
97	A string `tag1:"a,b" tag2:"c"`
98	B string `tag1:"a,c"`
99	C string `tag1:"b,c"`
100
101	D struct {
102		E string `tag1:"a,b" tag2:"c"`
103		F string `tag1:"a,c"`
104		G string `tag1:"b,c"`
105	} `tag1:"b,c"`
106}
107
108var pkgPath string
109var pkgFiles map[string][]string
110var moduleTypeNameFactories map[string]reflect.Value
111var moduleTypeNamePropertyStructs map[string][]interface{}
112
113func init() {
114	pc, filename, _, _ := runtime.Caller(0)
115	fn := runtime.FuncForPC(pc)
116
117	var err error
118	pkgPath, err = funcNameToPkgPath(fn.Name())
119	if err != nil {
120		panic(err)
121	}
122
123	pkgFiles = map[string][]string{
124		pkgPath: {filename},
125	}
126
127	factories := map[string]factoryFn{"foo": fooFactory, "bar": barFactory}
128
129	moduleTypeNameFactories = make(map[string]reflect.Value, len(factories))
130	moduleTypeNamePropertyStructs = make(map[string][]interface{}, len(factories))
131	for name, factory := range factories {
132		moduleTypeNameFactories[name] = reflect.ValueOf(factory)
133		_, structs := factory()
134		moduleTypeNamePropertyStructs[name] = structs
135	}
136}
137
138func TestModuleTypeDocs(t *testing.T) {
139	r := NewReader(pkgFiles)
140	for m := range moduleTypeNameFactories {
141		mt, err := r.ModuleType(m+"_module", moduleTypeNameFactories[m])
142		if err != nil {
143			t.Fatal(err)
144		}
145
146		expectedText := template.HTML(m + " docs.\n\n")
147		if mt.Text != expectedText {
148			t.Errorf("unexpected docs %q", mt.Text)
149		}
150
151		if mt.PkgPath != pkgPath {
152			t.Errorf("expected pkgpath %q, got %q", pkgPath, mt.PkgPath)
153		}
154	}
155}
156
157func TestPropertyStruct(t *testing.T) {
158	r := NewReader(pkgFiles)
159	ps, err := r.PropertyStruct(pkgPath, "props", reflect.ValueOf(props{A: "B"}))
160	if err != nil {
161		t.Fatal(err)
162	}
163
164	if ps.Text != "props docs.\n" {
165		t.Errorf("unexpected docs %q", ps.Text)
166	}
167	if len(ps.Properties) != 1 {
168		t.Fatalf("want 1 property, got %d", len(ps.Properties))
169	}
170
171	if ps.Properties[0].Name != "a" || ps.Properties[0].Text != "A docs.\n\n" || ps.Properties[0].Default != "B" {
172		t.Errorf("unexpected property docs %q %q %q",
173			ps.Properties[0].Name, ps.Properties[0].Text, ps.Properties[0].Default)
174	}
175}
176
177func TestPackage(t *testing.T) {
178	r := NewReader(pkgFiles)
179	pkg, err := r.Package(pkgPath)
180	if err != nil {
181		t.Fatal(err)
182	}
183
184	if pkg.Text != "bpdoc docs.\n" {
185		t.Errorf("unexpected docs %q", pkg.Text)
186	}
187}
188
189func TestFuncToPkgPath(t *testing.T) {
190	tests := []struct {
191		f    string
192		want string
193	}{
194		{
195			f:    "github.com/google/blueprint/bootstrap.Main",
196			want: "github.com/google/blueprint/bootstrap",
197		},
198		{
199			f:    "android/soong/android.GenruleFactory",
200			want: "android/soong/android",
201		},
202		{
203			f:    "android/soong/android.ModuleFactoryAdapter.func1",
204			want: "android/soong/android",
205		},
206		{
207			f:    "main.Main",
208			want: "main",
209		},
210	}
211	for _, tt := range tests {
212		t.Run(tt.f, func(t *testing.T) {
213			got, err := funcNameToPkgPath(tt.f)
214			if err != nil {
215				t.Fatal(err)
216			}
217			if got != tt.want {
218				t.Errorf("funcNameToPkgPath(%v) = %v, want %v", tt.f, got, tt.want)
219			}
220		})
221	}
222}
223