Web Inspector: InspectorTest should be a subclass of TestHarness
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Test / TestSuite.js
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 TestSuite = class TestSuite extends WebInspector.Object
27 {
28     constructor(harness, name) {
29         if (!(harness instanceof TestHarness))
30             throw new Error("Must pass the test's harness as the first argument.");
31
32         if (typeof name !== "string" || !name.trim().length)
33             throw new Error("Tried to create TestSuite without string suite name.");
34
35         super();
36
37         this.name = name;
38         this._harness = harness;
39
40         this.testcases = [];
41         this.runCount = 0;
42         this.failCount = 0;
43     }
44
45     // Use this if the test file only has one suite, and no handling
46     // of the value returned by runTestCases() is needed.
47     runTestCasesAndFinish()
48     {
49         throw new Error("Must be implemented by subclasses.");
50     }
51
52     runTestCases()
53     {
54         throw new Error("Must be implemented by subclasses.");
55     }
56
57     get passCount()
58     {
59         return this.runCount - this.failCount;
60     }
61
62     get skipCount()
63     {
64         if (this.failCount)
65             return this.testcases.length - this.runCount;
66         else
67             return 0;
68     }
69
70     addTestCase(testcase)
71     {
72         if (!testcase || !(testcase instanceof Object))
73             throw new Error("Tried to add non-object test case.");
74
75         if (typeof testcase.name !== "string" || !testcase.name.trim().length)
76             throw new Error("Tried to add test case without a name.");
77
78         if (typeof testcase.test !== "function")
79             throw new Error("Tried to add test case without `test` function.");
80
81         this.testcases.push(testcase);
82     }
83 };
84
85 AsyncTestSuite = class AsyncTestSuite extends TestSuite
86 {
87     runTestCasesAndFinish()
88     {
89         function finish() {
90             this._harness.completeTest();
91         }
92
93         this.runTestCases()
94             .then(finish.bind(this))
95             .catch(finish.bind(this));
96     }
97
98     runTestCases()
99     {
100         if (!this.testcases.length)
101             throw new Error("Tried to call runTestCases() for suite with no test cases");
102         if (this._startedRunning)
103             throw new Error("Tried to call runTestCases() more than once.");
104
105         this._startedRunning = true;
106
107         this._harness.log("");
108         this._harness.log("== Running test suite: " + this.name);
109
110         // Avoid adding newlines if nothing was logged.
111         let priorLogCount = this._harness.logCount;
112         let self = this;
113         let result = this.testcases.reduce(function(chain, testcase, i) {
114             return chain.then(function() {
115                 if (i > 0 && priorLogCount + 1 < self._harness.logCount)
116                     self._harness.log("");
117
118                 priorLogCount = self._harness.logCount;
119                 self._harness.log("-- Running test case: " + testcase.name);
120                 self.runCount++;
121                 return new Promise(testcase.test);
122             });
123         }, Promise.resolve());
124
125         return result.catch(function(e) {
126             self.failCount++;
127             let message = e;
128             if (e instanceof Error)
129                 message = e.message;
130
131             if (typeof message !== "string")
132                 message = JSON.stringify(message);
133
134             self._harness.log("!! EXCEPTION: " + message);
135             throw e; // Reject this promise by re-throwing the error.
136         });
137     }
138 };
139
140 SyncTestSuite = class SyncTestSuite extends TestSuite
141 {
142     runTestCasesAndFinish()
143     {
144         this.runTestCases();
145         this._harness.completeTest();
146     }
147
148     runTestCases()
149     {
150         if (!this.testcases.length)
151             throw new Error("Tried to call runTestCases() for suite with no test cases");
152         if (this._startedRunning)
153             throw new Error("Tried to call runTestCases() more than once.");
154
155         this._startedRunning = true;
156
157         this._harness.log("");
158         this._harness.log("== Running test suite: " + this.name);
159
160         let priorLogCount = this._harness.logCount;
161         let self = this;
162         for (let i = 0; i < this.testcases.length; i++) {
163             let testcase = this.testcases[i];
164             if (i > 0 && priorLogCount + 1 < this._harness.logCount)
165                 this._harness.log("");
166
167             priorLogCount = this._harness.logCount;
168
169             this._harness.log("-- Running test case: " + testcase.name);
170             self.runCount++;
171             try {
172                 let result = testcase.test.call(null);
173                 if (result === false) {
174                     self.failCount++;
175                     return false;
176                 }
177             } catch (e) {
178                 self.failCount++;
179                 let message = e;
180                 if (e instanceof Error)
181                     message = e.message;
182                 else
183                     e = new Error(e);
184
185                 if (typeof message !== "string")
186                     message = JSON.stringify(message);
187
188                 this._harness.log("!! EXCEPTION: " + message);
189                 return false;
190             }
191         }
192
193         return true;
194     }
195 };