Skip to content

Commit 529db17

Browse files
committed
Adapt to new spaghetti-cutter structure
1 parent 6632773 commit 529db17

File tree

7 files changed

+382
-29
lines changed

7 files changed

+382
-29
lines changed

analdata/analdata.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package analdata
2+
3+
import (
4+
"sort"
5+
"strings"
6+
7+
"github.com/flowdev/spaghetti-cutter/data"
8+
)
9+
10+
// PkgImports contains the package type and the imported internal packages with their types.
11+
type PkgImports struct {
12+
PkgType data.PkgType
13+
Imports map[string]data.PkgType
14+
}
15+
16+
// DependencyMap is mapping importing package to imported packages.
17+
// importingPackageName -> (importedPackageNames -> PkgType)
18+
// An imported package name could be added multiple times to the same importing
19+
// package name due to test packages.
20+
type DependencyMap map[string]PkgImports
21+
22+
// SortedPkgNames returns the sorted keys (package names) of the dependency map.
23+
func (dm DependencyMap) SortedPkgNames() []string {
24+
names := make([]string, 0, len(dm))
25+
for pkg := range dm {
26+
names = append(names, pkg)
27+
}
28+
sort.Strings(names)
29+
return names
30+
}
31+
32+
// FilterDepMap filters allMap to contain only packages matching idx and its transitive
33+
// dependencies. Entries matching other indices in links are filtered, too.
34+
func FilterDepMap(allMap DependencyMap, idx int, links data.PatternList) DependencyMap {
35+
if idx < 0 || len(links) == 0 {
36+
return allMap
37+
}
38+
39+
fltrMap := make(DependencyMap, len(allMap))
40+
for pkg := range allMap {
41+
if i := data.DocMatchStringIndex(pkg, links); i >= 0 && i == idx {
42+
copyDepsRecursive(allMap, pkg, fltrMap, links, idx)
43+
}
44+
}
45+
return fltrMap
46+
}
47+
func copyDepsRecursive(
48+
allMap DependencyMap,
49+
startPkg string,
50+
fltrMap DependencyMap,
51+
links data.PatternList,
52+
idx int,
53+
) {
54+
if i := data.DocMatchStringIndex(startPkg, links); i >= 0 && i != idx {
55+
return
56+
}
57+
imps, ok := allMap[startPkg]
58+
if !ok {
59+
return
60+
}
61+
fltrMap[startPkg] = imps
62+
for pkg := range imps.Imports {
63+
copyDepsRecursive(allMap, pkg, fltrMap, links, idx)
64+
}
65+
}
66+
67+
// PkgForPattern returns the (parent) package of the given package pattern.
68+
// If pkg doesn't contain any wildcard '*' the whole string is returned.
69+
// Otherwise everything up to the last '/' before the wildcard or
70+
// the empty string if there is no '/' before it.
71+
func PkgForPattern(pkg string) string {
72+
i := strings.IndexRune(pkg, '*')
73+
if i < 0 {
74+
return pkg
75+
}
76+
i = strings.LastIndex(pkg[:i], "/")
77+
if i > 0 {
78+
return pkg[:i]
79+
}
80+
return ""
81+
}

analdata/analdata_test.go

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
package analdata_test
2+
3+
import (
4+
"reflect"
5+
"sort"
6+
"strings"
7+
"testing"
8+
9+
"github.com/flowdev/spaghetti-analyzer/analdata"
10+
"github.com/flowdev/spaghetti-cutter/data"
11+
)
12+
13+
func TestDependencyMapSortedPkgNames(t *testing.T) {
14+
givenDepMap := map[string]analdata.PkgImports{
15+
"b": analdata.PkgImports{},
16+
"a": analdata.PkgImports{},
17+
"aa": analdata.PkgImports{},
18+
"b ": analdata.PkgImports{},
19+
"a ": analdata.PkgImports{},
20+
}
21+
expectedNames := []string{"a", "a ", "aa", "b", "b "}
22+
23+
actualNames := analdata.DependencyMap(givenDepMap).SortedPkgNames()
24+
if len(actualNames) != len(givenDepMap) {
25+
t.Errorf("expected %d names, actual %d",
26+
len(givenDepMap), len(actualNames))
27+
}
28+
if !reflect.DeepEqual(actualNames, expectedNames) {
29+
t.Errorf("expected names to be %q, got %q", expectedNames, actualNames)
30+
}
31+
}
32+
33+
func TestFilterDepMap(t *testing.T) {
34+
manyLinks, err := data.NewSimplePatternList([]string{"a", "b/c/d", "e**", "f/**"}, "test")
35+
if err != nil {
36+
t.Fatalf("got unexpected error: %v", err)
37+
}
38+
bigDepMap := analdata.DependencyMap{
39+
"a": analdata.PkgImports{
40+
PkgType: data.TypeGod,
41+
Imports: map[string]data.PkgType{
42+
"b/c/d": data.TypeTool,
43+
"epsilon": data.TypeTool,
44+
"escher": data.TypeTool,
45+
"z": data.TypeDB,
46+
"f": data.TypeStandard,
47+
},
48+
},
49+
"z": analdata.PkgImports{
50+
PkgType: data.TypeDB,
51+
Imports: map[string]data.PkgType{
52+
"b/c/d": data.TypeTool,
53+
"epsilon": data.TypeTool,
54+
"escher": data.TypeTool,
55+
"x": data.TypeDB,
56+
},
57+
},
58+
"x": analdata.PkgImports{
59+
PkgType: data.TypeDB,
60+
Imports: map[string]data.PkgType{
61+
"b/c/d": data.TypeTool,
62+
"escher": data.TypeTool,
63+
},
64+
},
65+
"m": analdata.PkgImports{
66+
PkgType: data.TypeStandard,
67+
Imports: map[string]data.PkgType{
68+
"b/c/d": data.TypeTool,
69+
"x": data.TypeDB,
70+
},
71+
},
72+
"f": analdata.PkgImports{
73+
PkgType: data.TypeStandard,
74+
Imports: map[string]data.PkgType{
75+
"f/g": data.TypeStandard,
76+
"f/h": data.TypeStandard,
77+
"f/i": data.TypeStandard,
78+
},
79+
},
80+
"f/g": analdata.PkgImports{
81+
PkgType: data.TypeStandard,
82+
Imports: map[string]data.PkgType{
83+
"f/j": data.TypeStandard,
84+
"escher": data.TypeTool,
85+
"x": data.TypeDB,
86+
},
87+
},
88+
"f/h": analdata.PkgImports{
89+
PkgType: data.TypeStandard,
90+
Imports: map[string]data.PkgType{
91+
"x": data.TypeDB,
92+
"m": data.TypeStandard,
93+
},
94+
},
95+
"f/i": analdata.PkgImports{
96+
PkgType: data.TypeStandard,
97+
Imports: map[string]data.PkgType{
98+
"escher": data.TypeTool,
99+
},
100+
},
101+
}
102+
t.Logf("bigDepMap:\n%s", prettyPrint(bigDepMap))
103+
104+
specs := []struct {
105+
name string
106+
givenIdx int
107+
givenLinks data.PatternList
108+
expectedDepMap string
109+
}{
110+
{
111+
name: "negative-index",
112+
givenIdx: -123,
113+
givenLinks: manyLinks,
114+
expectedDepMap: prettyPrint(bigDepMap),
115+
}, {
116+
name: "no-links",
117+
givenIdx: 0,
118+
givenLinks: data.PatternList{},
119+
expectedDepMap: prettyPrint(bigDepMap),
120+
}, {
121+
name: "no-wildcard-leave-package",
122+
givenIdx: 1,
123+
givenLinks: manyLinks,
124+
expectedDepMap: ``,
125+
}, {
126+
name: "no-wildcard-tree",
127+
givenIdx: 0,
128+
givenLinks: manyLinks,
129+
expectedDepMap: `a [G] imports: b/c/d [T]
130+
a [G] imports: epsilon [T]
131+
a [G] imports: escher [T]
132+
a [G] imports: f [S]
133+
a [G] imports: z [D]
134+
x [D] imports: b/c/d [T]
135+
x [D] imports: escher [T]
136+
z [D] imports: b/c/d [T]
137+
z [D] imports: epsilon [T]
138+
z [D] imports: escher [T]
139+
z [D] imports: x [D]`,
140+
}, {
141+
name: "wildcard-leave-packages",
142+
givenIdx: 2,
143+
givenLinks: manyLinks,
144+
expectedDepMap: ``,
145+
}, {
146+
name: "wildcard-tree",
147+
givenIdx: 3,
148+
givenLinks: manyLinks,
149+
expectedDepMap: `f [S] imports: f/g [S]
150+
f [S] imports: f/h [S]
151+
f [S] imports: f/i [S]
152+
f/g [S] imports: escher [T]
153+
f/g [S] imports: f/j [S]
154+
f/g [S] imports: x [D]
155+
f/h [S] imports: m [S]
156+
f/h [S] imports: x [D]
157+
f/i [S] imports: escher [T]
158+
m [S] imports: b/c/d [T]
159+
m [S] imports: x [D]
160+
x [D] imports: b/c/d [T]
161+
x [D] imports: escher [T]`,
162+
},
163+
}
164+
165+
for _, spec := range specs {
166+
t.Run(spec.name, func(t *testing.T) {
167+
actualDepMap := analdata.FilterDepMap(bigDepMap, spec.givenIdx, spec.givenLinks)
168+
sDeps := prettyPrint(actualDepMap)
169+
if sDeps != spec.expectedDepMap {
170+
failWithDiff(t, spec.expectedDepMap, sDeps)
171+
}
172+
})
173+
}
174+
}
175+
func prettyPrint(deps analdata.DependencyMap) string {
176+
sb := strings.Builder{}
177+
178+
for _, pkg := range deps.SortedPkgNames() {
179+
imps := deps[pkg]
180+
pkgTypeRune := data.TypeLetter(imps.PkgType)
181+
182+
for _, imp := range sortedImpNames(imps.Imports) {
183+
sb.WriteString(pkg)
184+
sb.WriteString(" [")
185+
sb.WriteRune(pkgTypeRune)
186+
sb.WriteString("] imports: ")
187+
sb.WriteString(imp)
188+
sb.WriteString(" [")
189+
sb.WriteRune(data.TypeLetter(imps.Imports[imp]))
190+
sb.WriteString("]\n")
191+
}
192+
}
193+
194+
return sb.String()
195+
}
196+
func failWithDiff(t *testing.T, expected, actual string) {
197+
exps := strings.Split(expected, "\n")
198+
acts := strings.Split(actual, "\n")
199+
200+
i := 0
201+
j := 0
202+
n := len(exps) - 1
203+
m := len(acts) - 1
204+
if n >= 0 && exps[n] == "" {
205+
n--
206+
}
207+
if m >= 0 && acts[m] == "" {
208+
m--
209+
}
210+
for i <= n && j <= m {
211+
if exps[i] < acts[j] {
212+
t.Errorf("expected but missing: %s", exps[i])
213+
i++
214+
} else if exps[i] == acts[j] {
215+
i++
216+
j++
217+
} else if exps[i] > acts[j] {
218+
t.Errorf("actual but unexpected: %s", acts[j])
219+
j++
220+
}
221+
}
222+
for ; i <= n; i++ {
223+
t.Errorf("expected but missing: %s", exps[i])
224+
}
225+
for ; j <= m; j++ {
226+
t.Errorf("actual but unexpected: %s", acts[j])
227+
}
228+
}
229+
func sortedImpNames(imps map[string]data.PkgType) []string {
230+
names := make([]string, 0, len(imps))
231+
for imp := range imps {
232+
names = append(names, imp)
233+
}
234+
sort.Strings(names)
235+
return names
236+
}
237+
238+
func TestPkgForPattern(t *testing.T) {
239+
specs := []struct {
240+
name string
241+
givenPattern string
242+
expectedPkg string
243+
}{
244+
{
245+
name: "no-wildcard",
246+
givenPattern: "abc/def/ghi",
247+
expectedPkg: "abc/def/ghi",
248+
}, {
249+
name: "one-simple-wildcard",
250+
givenPattern: "abc/def/*",
251+
expectedPkg: "abc/def",
252+
}, {
253+
name: "one-middle-wildcard",
254+
givenPattern: "abc/def/gh*i",
255+
expectedPkg: "abc/def",
256+
}, {
257+
name: "many-wildcards",
258+
givenPattern: "abc/d**e*f/*",
259+
expectedPkg: "abc",
260+
}, {
261+
name: "no-slash",
262+
givenPattern: "abc*",
263+
expectedPkg: "",
264+
},
265+
}
266+
for _, spec := range specs {
267+
t.Run(spec.name, func(t *testing.T) {
268+
actualPkg := analdata.PkgForPattern(spec.givenPattern)
269+
if actualPkg != spec.expectedPkg {
270+
t.Errorf("expected package %q, actual %q", spec.expectedPkg, actualPkg)
271+
}
272+
})
273+
}
274+
}

deps/deps.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package deps
33
import (
44
"strings"
55

6+
"github.com/flowdev/spaghetti-analyzer/analdata"
67
"github.com/flowdev/spaghetti-analyzer/x/pkgs"
78
"github.com/flowdev/spaghetti-cutter/data"
89
"github.com/flowdev/spaghetti-cutter/x/config"
910
)
1011

1112
// Fill fills the dependency map of the given package.
12-
func Fill(pkg *pkgs.Package, rootPkg string, cfg config.Config, depMap *data.DependencyMap) {
13+
func Fill(pkg *pkgs.Package, rootPkg string, cfg config.Config, depMap *analdata.DependencyMap) {
1314
if pkgs.IsTestPackage(pkg) {
1415
return
1516
}
@@ -37,8 +38,8 @@ func importsOf(
3738
pkg *pkgs.Package,
3839
relPkg, strictRelPkg, rootPkg string,
3940
cfg config.Config,
40-
) data.PkgImports {
41-
imps := data.PkgImports{}
41+
) analdata.PkgImports {
42+
imps := analdata.PkgImports{}
4243

4344
for _, p := range pkg.Imports {
4445
if !strings.HasPrefix(p.PkgPath, rootPkg) {

0 commit comments

Comments
 (0)