9
"github.com/ajstarks/svgo"
10
jc "github.com/juju/testing/checkers"
11
gc "gopkg.in/check.v1"
13
"gopkg.in/juju/jujusvg.v1/assets"
16
type CanvasSuite struct{}
18
var _ = gc.Suite(&CanvasSuite{})
20
func (s *CanvasSuite) TestApplicationRender(c *gc.C) {
21
// Ensure that the Application's definition and usage methods output the
22
// proper SVG elements.
23
var tests = []struct {
25
application application
29
about: "Application without iconSrc, no def created",
30
application: application{
38
expected: `<g transform="translate(0,0)" >
40
<circle cx="90" cy="90" r="90" class="application-block" fill="#f5f5f5" stroke="#888" stroke-width="1" />
41
<image x="42" y="42" width="96" height="96" xlink:href="foo" clip-path="url(#clip-mask)" />
42
<rect x="0" y="135" width="180" height="32" rx="2" ry="2" fill="rgba(220, 220, 220, 0.8)" />
43
<text x="90" y="157" text-anchor="middle" style="font-weight:200" >foo</text>
48
about: "Application with iconSrc",
49
application: application{
56
iconSrc: []byte("<svg>bar</svg>"),
58
expected: `<svg:svg xmlns:svg="http://www.w3.org/2000/svg" id="icon-1">bar</svg:svg><g transform="translate(0,0)" >
60
<circle cx="90" cy="90" r="90" class="application-block" fill="#f5f5f5" stroke="#888" stroke-width="1" />
61
<use x="0" y="0" xlink:href="#icon-1" transform="translate(42,42)" width="96" height="96" clip-path="url(#clip-mask)" />
62
<rect x="0" y="135" width="180" height="32" rx="2" ry="2" fill="rgba(220, 220, 220, 0.8)" />
63
<text x="90" y="157" text-anchor="middle" style="font-weight:200" >bar</text>
68
about: "Application with already def'd icon",
69
application: application{
76
iconSrc: []byte("<svg>bar</svg>"),
78
expected: `<g transform="translate(0,0)" >
80
<circle cx="90" cy="90" r="90" class="application-block" fill="#f5f5f5" stroke="#888" stroke-width="1" />
81
<use x="0" y="0" xlink:href="#icon-1" transform="translate(42,42)" width="96" height="96" clip-path="url(#clip-mask)" />
82
<rect x="0" y="135" width="180" height="32" rx="2" ry="2" fill="rgba(220, 220, 220, 0.8)" />
83
<text x="90" y="157" text-anchor="middle" style="font-weight:200" >baz</text>
88
// Maintain our list of rendered icons outside the loop.
89
iconsRendered := make(map[string]bool)
90
iconIds := make(map[string]string)
91
for _, test := range tests {
94
test.application.definition(svg, iconsRendered, iconIds)
95
test.application.usage(svg, iconIds)
98
c.Assert(buf.String(), gc.Equals, test.expected)
102
func (s *CanvasSuite) TestRelationRender(c *gc.C) {
103
// Ensure that the Relation's definition and usage methods output the
104
// proper SVG elements.
107
relation := applicationRelation{
109
applicationA: &application{
115
applicationB: &application{
122
relation.definition(svg)
124
c.Assert(buf.String(), gc.Equals,
127
<line x1="90" y1="90" x2="190" y2="190" stroke="#a7a7a7" stroke-width="1px" stroke-dasharray="62.71, 16" />
128
<use x="132" y="132" xlink:href="#healthCircle" />
129
<circle cx="153" cy="153" r="4" fill="#a7a7a7" />
130
<circle cx="126" cy="126" r="4" fill="#a7a7a7" />
135
func (s *CanvasSuite) TestIconClipPath(c *gc.C) {
136
// Ensure that the icon ClipPath returns the correctly sizes clipping Circle
140
canvas.iconClipPath(svg)
141
c.Assert(buf.String(), gc.Equals,
142
`<circle cx="47" cy="49" r="45" id="application-icon-mask" fill="none" />
143
<clipPath id="clip-mask" ><use x="0" y="0" xlink:href="#application-icon-mask" />
148
func (s *CanvasSuite) TestLayout(c *gc.C) {
149
// Ensure that the SVG is sized exactly around the positioned applications.
151
canvas.addApplication(&application{
152
name: "application1",
158
canvas.addApplication(&application{
159
name: "application2",
165
width, height := canvas.layout()
166
c.Assert(width, gc.Equals, 281)
167
c.Assert(height, gc.Equals, 281)
168
canvas.addApplication(&application{
169
name: "application3",
175
canvas.addApplication(&application{
176
name: "application4",
182
canvas.addApplication(&application{
183
name: "application5",
189
width, height = canvas.layout()
190
c.Assert(width, gc.Equals, 481)
191
c.Assert(height, gc.Equals, 381)
194
func (s *CanvasSuite) TestMarshal(c *gc.C) {
195
// Ensure that the internal representation of the canvas can be marshalled
199
applicationA := &application{
200
name: "application-a",
201
charmPath: "trusty/svc-a",
207
<svg xmlns="http://www.w3.org/2000/svg" class="blah">
208
<circle cx="20" cy="20" r="20" style="fill:#000" />
211
applicationB := &application{
212
name: "application-b",
218
canvas.addApplication(applicationA)
219
canvas.addApplication(applicationB)
220
canvas.addRelation(&applicationRelation{
222
applicationA: applicationA,
223
applicationB: applicationB,
226
c.Logf("%s", buf.Bytes())
227
assertXMLEqual(c, buf.Bytes(), []byte(`
228
<?xml version="1.0"?>
229
<!-- Generated by SVGo -->
230
<svg width="281" height="281"
231
style="font-family:Ubuntu, sans-serif;" viewBox="0 0 281 281"
232
xmlns="http://www.w3.org/2000/svg"
233
xmlns:xlink="http://www.w3.org/1999/xlink">
235
<g id="healthCircle" transform="scale(1.1)" >`+assets.RelationIconHealthy+`
237
<svg xmlns="http://www.w3.org/2000/svg" class="blah" id="icon-1">
238
				<circle cx="20" cy="20" r="20" style="fill:#000"></circle>
239
			</svg></defs>
240
<circle cx="47" cy="49" r="45" id="application-icon-mask" fill="none" />
241
<clipPath id="clip-mask" ><use x="0" y="0" xlink:href="#application-icon-mask" />
245
<title>relation</title>
246
<line x1="90" y1="90" x2="190" y2="190" stroke="#a7a7a7" stroke-width="1px" stroke-dasharray="62.71, 16" />
247
<use x="132" y="132" xlink:href="#healthCircle" />
248
<circle cx="153" cy="153" r="4" fill="#a7a7a7" />
249
<circle cx="126" cy="126" r="4" fill="#a7a7a7" />
252
<g id="applications">
253
<g transform="translate(0,0)" >
254
<title>application-a</title>
255
<circle cx="90" cy="90" r="90" class="application-block" fill="#f5f5f5" stroke="#888" stroke-width="1" />
256
<use x="0" y="0" xlink:href="#icon-1" transform="translate(42,42)" width="96" height="96" clip-path="url(#clip-mask)" />
257
<rect x="0" y="135" width="180" height="32" rx="2" ry="2" fill="rgba(220, 220, 220, 0.8)" />
258
<text x="90" y="157" text-anchor="middle" style="font-weight:200" >application-a</text>
260
<g transform="translate(100,100)" >
261
<title>application-b</title>
262
<circle cx="90" cy="90" r="90" class="application-block" fill="#f5f5f5" stroke="#888" stroke-width="1" />
263
<image x="42" y="42" width="96" height="96" xlink:href="" clip-path="url(#clip-mask)" />
264
<rect x="0" y="135" width="180" height="32" rx="2" ry="2" fill="rgba(220, 220, 220, 0.8)" />
265
<text x="90" y="157" text-anchor="middle" style="font-weight:200" >application-b</text>
272
func assertXMLEqual(c *gc.C, obtained, expected []byte) {
273
toksObtained := xmlTokens(c, obtained)
274
toksExpected := xmlTokens(c, expected)
275
c.Assert(toksObtained, jc.DeepEquals, toksExpected)
278
func xmlTokens(c *gc.C, data []byte) []xml.Token {
279
dec := xml.NewDecoder(bytes.NewReader(data))
282
tok, err := dec.Token()
286
c.Assert(err, gc.IsNil)
288
if cdata, ok := tok.(xml.CharData); ok {
289
// It's char data - trim all white space and ignore it
290
// if it's all blank.
291
cdata = bytes.TrimSpace(cdata)
297
toks = append(toks, xml.CopyToken(tok))