Slightly scary change to fix issue 426
[dygraphs.git] / auto_tests / tests / CanvasAssertions.js
1 // Copyright (c) 2011 Google, Inc.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20
21 /**
22 * @fileoverview Assertions and other code used to test a canvas proxy.
23 *
24 * @author konigsberg@google.com (Robert Konigsberg)
25 */
26
27 var CanvasAssertions = {};
28
29 /**
30 * Assert that a line is drawn between the two points
31 *
32 * This merely looks for one of these four possibilities:
33 * moveTo(p1) -> lineTo(p2)
34 * moveTo(p2) -> lineTo(p1)
35 * lineTo(p1) -> lineTo(p2)
36 * lineTo(p2) -> lineTo(p1)
37 *
38 * predicate is meant to be used when you want to track things like
39 * color and stroke width. It can either be a hash of context properties,
40 * or a function that accepts the current call.
41 */
42 CanvasAssertions.assertLineDrawn = function(proxy, p1, p2, predicate) {
43 // found = 1 when prior loop found p1.
44 // found = 2 when prior loop found p2.
45 var priorFound = 0;
46 for (var i = 0; i < proxy.calls__.length; i++) {
47 var call = proxy.calls__[i];
48
49 // This disables lineTo -> moveTo pairs.
50 if (call.name == "moveTo" && priorFound > 0) {
51 priorFound = 0;
52 }
53
54 var found = 0;
55 if (call.name == "moveTo" || call.name == "lineTo") {
56 var matchp1 = CanvasAssertions.matchPixels(p1, call.args);
57 var matchp2 = CanvasAssertions.matchPixels(p2, call.args);
58 if (matchp1 || matchp2) {
59 if (priorFound == 1 && matchp2) {
60 if (CanvasAssertions.match(predicate, call)) {
61 return;
62 }
63 }
64 if (priorFound == 2 && matchp1) {
65 if (CanvasAssertions.match(predicate, call.properties)) {
66 return;
67 }
68 }
69 found = matchp1 ? 1 : 2;
70 }
71 }
72 priorFound = found;
73 }
74
75 var toString = function(x) {
76 var s = "{";
77 for (var prop in x) {
78 if (x.hasOwnProperty(prop)) {
79 if (s.length > 1) {
80 s = s + ", ";
81 }
82 s = s + prop + ": " + x[prop];
83 }
84 }
85 return s + "}";
86 };
87 fail("Can't find a line drawn between " + p1 +
88 " and " + p2 + " with attributes " + toString(predicate));
89 };
90
91 /**
92 * Return the lines drawn with specific attributes.
93 *
94 * This merely looks for one of these four possibilities:
95 * moveTo(p1) -> lineTo(p2)
96 * moveTo(p2) -> lineTo(p1)
97 * lineTo(p1) -> lineTo(p2)
98 * lineTo(p2) -> lineTo(p1)
99 *
100 * attrs is meant to be used when you want to track things like
101 * color and stroke width.
102 */
103 CanvasAssertions.getLinesDrawn = function(proxy, predicate) {
104 var lastCall;
105 var lines = [];
106 for (var i = 0; i < proxy.calls__.length; i++) {
107 var call = proxy.calls__[i];
108
109 if (call.name == "lineTo") {
110 if (lastCall != null) {
111 if (CanvasAssertions.match(predicate, call)) {
112 lines.push([lastCall, call]);
113 }
114 }
115 }
116
117 lastCall = (call.name === "lineTo" || call.name === "moveTo") ? call : null;
118 }
119 return lines;
120 };
121
122 /**
123 * Verifies that every call to context.save() has a matching call to
124 * context.restore().
125 */
126 CanvasAssertions.assertBalancedSaveRestore = function(proxy) {
127 var depth = 0;
128 for (var i = 0; i < proxy.calls__.length; i++) {
129 var call = proxy.calls__[i];
130 if (call.name == "save") depth++
131 if (call.name == "restore") {
132 if (depth == 0) {
133 fail("Too many calls to restore()");
134 }
135 depth--;
136 }
137 }
138
139 if (depth > 0) {
140 fail("Missing matching 'context.restore()' calls.");
141 }
142 };
143
144 /**
145 * Checks how many lines of the given color have been drawn.
146 * @return {Integer} The number of lines of the given color.
147 */
148 // TODO(konigsberg): change 'color' to predicate? color is the
149 // common case. Possibly allow predicate to be function, hash, or
150 // string representing color?
151 CanvasAssertions.numLinesDrawn = function(proxy, color) {
152 var num_lines = 0;
153 for (var i = 0; i < proxy.calls__.length; i++) {
154 var call = proxy.calls__[i];
155 if (call.name == "lineTo" && call.properties.strokeStyle == color) {
156 num_lines++;
157 }
158 }
159 return num_lines;
160 };
161
162 /**
163 * Asserts that a series of lines are connected. For example,
164 * assertConsecutiveLinesDrawn(proxy, [[x1, y1], [x2, y2], [x3, y3]], predicate)
165 * is shorthand for
166 * assertLineDrawn(proxy, [x1, y1], [x2, y2], predicate)
167 * assertLineDrawn(proxy, [x2, y2], [x3, y3], predicate)
168 */
169 CanvasAssertions.assertConsecutiveLinesDrawn = function(proxy, segments, predicate) {
170 for (var i = 0; i < segments.length - 1; i++) {
171 CanvasAssertions.assertLineDrawn(proxy, segments[i], segments[i+1], predicate);
172 }
173 }
174
175 CanvasAssertions.matchPixels = function(expected, actual) {
176 // Expect array of two integers. Assuming the values are within one
177 // integer unit of each other. This should be tightened down by someone
178 // who knows what pixel a value of 5.8888 results in.
179 return Math.abs(expected[0] - actual[0]) < 1 &&
180 Math.abs(expected[1] - actual[1]) < 1;
181 };
182
183 /**
184 * For matching a proxy call against defined conditions.
185 * predicate can either by a hash of items compared against call.properties,
186 * or it can be a function that accepts the call, and returns true or false.
187 * If it's null, this function returns true.
188 */
189 CanvasAssertions.match = function(predicate, call) {
190 if (predicate === null) {
191 return true;
192 }
193 if (typeof(predicate) === "function") {
194 return predicate(call);
195 } else {
196 for (var attr in predicate) {
197 if (predicate.hasOwnProperty(attr) && predicate[attr] != call.properties[attr]) {
198 return false;
199 }
200 }
201 }
202 return true;
203 };