1 package d2chaos_test
2
3 import (
4 "context"
5 "fmt"
6 "io/ioutil"
7 "os"
8 "path/filepath"
9 "runtime/debug"
10 "strconv"
11 "strings"
12 "testing"
13
14 "github.com/stretchr/testify/assert"
15
16 "oss.terrastruct.com/d2/d2chaos"
17 "oss.terrastruct.com/d2/d2compiler"
18 "oss.terrastruct.com/d2/d2exporter"
19 "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
20 "oss.terrastruct.com/d2/lib/log"
21 "oss.terrastruct.com/d2/lib/textmeasure"
22 )
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 func TestD2Chaos(t *testing.T) {
42 t.Parallel()
43
44 const outDir = "out"
45 err := os.MkdirAll(outDir, 0755)
46 if err != nil {
47 t.Fatal(err)
48 }
49 t.Logf("writing generated files to %s", outDir)
50
51 t.Run("pinned", func(t *testing.T) {
52 testPinned(t, outDir)
53 })
54
55 n := 1
56 if os.Getenv("D2_CHAOS_N") != "" {
57 envn, err := strconv.Atoi(os.Getenv("D2_CHAOS_N"))
58 if err != nil {
59 t.Errorf("failed to atoi $D2_CHAOS_N: %v", err)
60 } else {
61 n = envn
62 }
63 }
64
65 maxi := 10
66 if os.Getenv("D2_CHAOS_MAXI") != "" {
67 envMaxi, err := strconv.Atoi(os.Getenv("D2_CHAOS_MAXI"))
68 if err != nil {
69 t.Errorf("failed to atoi $D2_CHAOS_MAXI: %v", err)
70 } else {
71 maxi = envMaxi
72 }
73 }
74
75 for i := 0; i < n; i++ {
76 i := i
77 t.Run("", func(t *testing.T) {
78 t.Parallel()
79
80 text, err := d2chaos.GenDSL(maxi)
81 if err != nil {
82 t.Fatal(err)
83 }
84
85 textPath := filepath.Join(outDir, fmt.Sprintf("%d.d2", i))
86 test(t, textPath, text)
87 })
88 }
89 }
90
91 func test(t *testing.T, textPath, text string) {
92 t.Logf("writing d2 to %v (%d bytes)", textPath, len(text))
93 err := ioutil.WriteFile(textPath, []byte(text), 0644)
94 if err != nil {
95 t.Fatal(err)
96 }
97
98 goencText := fmt.Sprintf("%#v", text)
99 t.Logf("writing d2.goenc to %v (%d bytes)", textPath+".goenc", len(goencText))
100 err = ioutil.WriteFile(textPath+".goenc", []byte(goencText), 0644)
101 if err != nil {
102 t.Fatal(err)
103 }
104
105 g, _, err := d2compiler.Compile("", strings.NewReader(text), nil)
106 if err != nil {
107 t.Fatal(err)
108 }
109
110 t.Run("layout", func(t *testing.T) {
111 defer func() {
112 r := recover()
113 if r != nil {
114 t.Errorf("recovered layout engine panic: %#v\n%s", r, debug.Stack())
115 }
116 }()
117
118 ctx := log.WithTB(context.Background(), t, nil)
119
120 ruler, err := textmeasure.NewRuler()
121 assert.Nil(t, err)
122
123 err = g.SetDimensions(nil, ruler, nil)
124 assert.Nil(t, err)
125
126 err = d2dagrelayout.DefaultLayout(ctx, g)
127 if err != nil {
128 t.Fatal(err)
129 }
130
131 _, err = d2exporter.Export(ctx, g, nil)
132 if err != nil {
133 t.Fatal(err)
134 }
135 })
136 }
137
138 func testPinned(t *testing.T, outDir string) {
139 t.Parallel()
140
141 outDir = filepath.Join(outDir, t.Name())
142 err := os.MkdirAll(outDir, 0755)
143 if err != nil {
144 t.Fatal(err)
145 }
146 t.Logf("writing generated files to %v", outDir)
147
148 testCases := []struct {
149 name string
150 text string
151 }{
152 {
153 name: "internal class edge",
154 text: "a: {\n shape: class\n b -> c\n }",
155 },
156 {
157 name: "table edge order",
158 text: "B: {shape: sql_table}\n B.C -- D\n B.C -- D\n D -- B.C\n B -- D\n A -- D",
159 },
160 {
161 name: "child to container edge",
162 text: "a.b -> a",
163 },
164 {
165 name: "sequence",
166 text: "a: {shape: step}\nb: {shape: step}\na -> b\n",
167 },
168 {
169 name: "orientation",
170 text: "a: {\n b\n c\n }\n a <- a.c\n a.b -> a\n",
171 },
172 {
173 name: "cannot create edge between boards",
174 text: `"" <-> ""`,
175 },
176 }
177
178 for _, tc := range testCases {
179 tc := tc
180 t.Run(tc.name, func(t *testing.T) {
181 t.Parallel()
182
183 textPath := filepath.Join(outDir, fmt.Sprintf("%s.d2", tc.name))
184 test(t, textPath, tc.text)
185 })
186 }
187 }
188
View as plain text