Add some power to the canvas assertions. Also, the canvas assertions weren't actually...
[dygraphs.git] / auto_tests / tests / CanvasAssertions.js
CommitLineData
718ad8e2 1// Copyright (c) 2011 Google, Inc.
644eff8b
RK
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
27var 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 *
bbb9a8f3
RK
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.
644eff8b 41 */
bbb9a8f3 42CanvasAssertions.assertLineDrawn = function(proxy, p1, p2, predicate) {
644eff8b
RK
43 // found = 1 when prior loop found p1.
44 // found = 2 when prior loop found p2.
45 var priorFound = 0;
7165f97b
RK
46 for (var i = 0; i < proxy.calls__.length; i++) {
47 var call = proxy.calls__[i];
644eff8b
RK
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) {
bbb9a8f3
RK
60 if (CanvasAssertions.match(predicate, call)) {
61 return;
62 }
644eff8b
RK
63 }
64 if (priorFound == 2 && matchp1) {
bbb9a8f3
RK
65 if (CanvasAssertions.match(predicate, call.properties)) {
66 return;
67 }
644eff8b
RK
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(attrs));
6278f6fe
DV
89};
90
91/**
8337da0a
RK
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 */
bbb9a8f3 103CanvasAssertions.getLinesDrawn = function(proxy, predicate) {
8337da0a
RK
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) {
bbb9a8f3
RK
111 if (!predicate || predicate(lastCall, call)) {
112 lines.push([lastCall, call]);
113 }
8337da0a
RK
114 }
115 }
116
117 lastCall = (call.name === "lineTo" || call.name === "moveTo") ? call : null;
118 }
119 return lines;
120};
121
122/**
6278f6fe
DV
123 * Verifies that every call to context.save() has a matching call to
124 * context.restore().
125 */
126CanvasAssertions.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};
644eff8b 143
063e83ba
DV
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 */
bbb9a8f3
RK
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?
063e83ba
DV
151CanvasAssertions.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;
6278f6fe 160};
063e83ba 161
644eff8b
RK
162CanvasAssertions.matchPixels = function(expected, actual) {
163 // Expect array of two integers. Assuming the values are within one
164 // integer unit of each other. This should be tightened down by someone
165 // who knows what pixel a value of 5.8888 results in.
166 return Math.abs(expected[0] - actual[0]) < 1 &&
167 Math.abs(expected[1] - actual[1]) < 1;
6278f6fe 168};
644eff8b 169
bbb9a8f3
RK
170/**
171 * For matching a proxy call against defined conditions.
172 * predicate can either by a hash of items compared against call.properties,
173 * or it can be a function that accepts the call, and returns true or false.
174 */
175CanvasAssertions.match = function(predicate, call) {
176 if (typeof(predicate) === "function") {
177 return predicate(call);
178 } else {
179 for (var attr in predicate) {
180 if (predicate.hasOwnProperty(attr) && predicate[attr] != call.properties[attr]) {
181 return false;
182 }
644eff8b
RK
183 }
184 }
185 return true;
6278f6fe 186};