Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsapi-tests / tests.h
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=99:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is JSAPI tests.
18  *
19  * The Initial Developer of the Original Code is
20  * Mozilla Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 2009
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *     Jason Orendorff
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40
41 #include "jsapi.h"
42 #include "jsprvtd.h"
43 #include "jsvector.h"
44 #include <errno.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48
49 class jsvalRoot
50 {
51   public:
52     explicit jsvalRoot(JSContext *context, jsval value = JSVAL_NULL)
53         : cx(context), v(value)
54     {
55         if (!JS_AddValueRoot(cx, &v)) {
56             fprintf(stderr, "Out of memory in jsvalRoot constructor, aborting\n");
57             abort();
58         }
59     }
60
61     ~jsvalRoot() { JS_RemoveValueRoot(cx, &v); }
62
63     operator jsval() const { return value(); }
64
65     jsvalRoot & operator=(jsval value) {
66         v = value;
67         return *this;
68     }
69
70     jsval * addr() { return &v; }
71     jsval value() const { return v; }
72
73   private:
74     JSContext *cx;
75     jsval v;
76 };
77
78 /* Note: Aborts on OOM. */
79 class JSAPITestString {
80     js::Vector<char, 0, js::SystemAllocPolicy> chars;
81   public:
82     JSAPITestString() {}
83     JSAPITestString(const char *s) { *this += s; }
84     JSAPITestString(const JSAPITestString &s) { *this += s; }
85
86     const char *begin() const { return chars.begin(); }
87     const char *end() const { return chars.end(); }
88     size_t length() const { return chars.length(); }
89
90     JSAPITestString & operator +=(const char *s) {
91         if (!chars.append(s, strlen(s)))
92             abort();
93         return *this;
94     }
95
96     JSAPITestString & operator +=(const JSAPITestString &s) {
97         if (!chars.append(s.begin(), s.length()))
98             abort();
99         return *this;
100     }
101 };
102
103 inline JSAPITestString operator+(JSAPITestString a, const char *b) { return a += b; }
104 inline JSAPITestString operator+(JSAPITestString a, const JSAPITestString &b) { return a += b; }
105
106 class JSAPITest
107 {
108   public:
109     static JSAPITest *list;
110     JSAPITest *next;
111
112     JSRuntime *rt;
113     JSContext *cx;
114     JSObject *global;
115     bool knownFail;
116     JSAPITestString msgs;
117     JSCrossCompartmentCall *call;
118
119     JSAPITest() : rt(NULL), cx(NULL), global(NULL), knownFail(false), call(NULL) {
120         next = list;
121         list = this;
122     }
123
124     virtual ~JSAPITest() { uninit(); }
125
126     virtual bool init() {
127         rt = createRuntime();
128         if (!rt)
129             return false;
130         cx = createContext();
131         if (!cx)
132             return false;
133         JS_BeginRequest(cx);
134         global = createGlobal();
135         if (!global)
136             return false;
137         call = JS_EnterCrossCompartmentCall(cx, global);
138         return call != NULL;
139     }
140
141     virtual void uninit() {
142         if (call) {
143             JS_LeaveCrossCompartmentCall(call);
144             call = NULL;
145         }
146         if (cx) {
147             JS_EndRequest(cx);
148             JS_DestroyContext(cx);
149             cx = NULL;
150         }
151         if (rt) {
152             destroyRuntime();
153             rt = NULL;
154         }
155     }
156
157     virtual const char * name() = 0;
158     virtual bool run() = 0;
159
160 #define EXEC(s) do { if (!exec(s, __FILE__, __LINE__)) return false; } while (false)
161
162     bool exec(const char *bytes, const char *filename, int lineno) {
163         jsvalRoot v(cx);
164         return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, v.addr()) ||
165                fail(bytes, filename, lineno);
166     }
167
168 #define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
169
170     bool evaluate(const char *bytes, const char *filename, int lineno, jsval *vp) {
171         return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, vp) ||
172                fail(bytes, filename, lineno);
173     }
174
175     JSAPITestString toSource(jsval v) {
176         JSString *str = JS_ValueToSource(cx, v);
177         if (str) {
178             JSAutoByteString bytes(cx, str);
179             if (!!bytes)
180                 return JSAPITestString(bytes.ptr());
181         }
182         JS_ClearPendingException(cx);
183         return JSAPITestString("<<error converting value to string>>");
184     }
185
186 #define CHECK_SAME(actual, expected) \
187     do { \
188         if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
189             return false; \
190     } while (false)
191
192     bool checkSame(jsval actual, jsval expected,
193                    const char *actualExpr, const char *expectedExpr,
194                    const char *filename, int lineno) {
195         JSBool same;
196         return (JS_SameValue(cx, actual, expected, &same) && same) ||
197                fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
198                     actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
199                     toSource(actual) + ", " + toSource(expected) + ")", filename, lineno);
200     }
201
202 #define CHECK(expr) \
203     do { \
204         if (!(expr)) \
205             return fail("CHECK failed: " #expr, __FILE__, __LINE__); \
206     } while (false)
207
208     bool fail(JSAPITestString msg = JSAPITestString(), const char *filename = "-", int lineno = 0) {
209         if (JS_IsExceptionPending(cx)) {
210             jsvalRoot v(cx);
211             JS_GetPendingException(cx, v.addr());
212             JS_ClearPendingException(cx);
213             JSString *s = JS_ValueToString(cx, v);
214             if (s) {
215                 JSAutoByteString bytes(cx, s);
216                 if (!!bytes)
217                     msg += bytes.ptr();
218             }
219         }
220         fprintf(stderr, "%s:%d:%.*s\n", filename, lineno, (int) msg.length(), msg.begin());
221         msgs += msg;
222         return false;
223     }
224
225     JSAPITestString messages() const { return msgs; }
226
227     static JSClass * basicGlobalClass() {
228         static JSClass c = {
229             "global", JSCLASS_GLOBAL_FLAGS,
230             JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
231             JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
232             JSCLASS_NO_OPTIONAL_MEMBERS
233         };
234         return &c;
235     }
236
237   protected:
238     static JSBool
239     print(JSContext *cx, uintN argc, jsval *vp)
240     {
241         jsval *argv = JS_ARGV(cx, vp);
242         for (uintN i = 0; i < argc; i++) {
243             JSString *str = JS_ValueToString(cx, argv[i]);
244             if (!str)
245                 return JS_FALSE;
246             char *bytes = JS_EncodeString(cx, str);
247             if (!bytes)
248                 return JS_FALSE;
249             printf("%s%s", i ? " " : "", bytes);
250             JS_free(cx, bytes);
251         }
252
253         putchar('\n');
254         fflush(stdout);
255         JS_SET_RVAL(cx, vp, JSVAL_VOID);
256         return JS_TRUE;
257     }
258
259     bool definePrint() {
260         return JS_DefineFunction(cx, global, "print", (JSNative) print, 0, 0);
261     }
262
263     virtual JSRuntime * createRuntime() {
264         return JS_NewRuntime(8L * 1024 * 1024);
265     }
266
267     virtual void destroyRuntime() {
268         JS_ASSERT(!cx);
269         JS_ASSERT(rt);
270         JS_DestroyRuntime(rt);
271     }
272
273     static void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
274         fprintf(stderr, "%s:%u:%s\n",
275                 report->filename ? report->filename : "<no filename>",
276                 (unsigned int) report->lineno,
277                 message);
278     }
279
280     virtual JSContext * createContext() {
281         JSContext *cx = JS_NewContext(rt, 8192);
282         if (!cx)
283             return NULL;
284         JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT);
285         JS_SetVersion(cx, JSVERSION_LATEST);
286         JS_SetErrorReporter(cx, &reportError);
287         return cx;
288     }
289
290     virtual JSClass * getGlobalClass() {
291         return basicGlobalClass();
292     }
293
294     virtual JSObject * createGlobal() {
295         /* Create the global object. */
296         JSObject *global = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL);
297         if (!global)
298             return NULL;
299
300         JSAutoEnterCompartment ac;
301         if (!ac.enter(cx, global))
302             return NULL;
303
304         /* Populate the global object with the standard globals,
305            like Object and Array. */
306         if (!JS_InitStandardClasses(cx, global))
307             return NULL;
308         return global;
309     }
310 };
311
312 #define BEGIN_TEST(testname)                                            \
313     class cls_##testname : public JSAPITest {                           \
314       public:                                                           \
315         virtual const char * name() { return #testname; }               \
316         virtual bool run()
317
318 #define END_TEST(testname)                                              \
319     };                                                                  \
320     static cls_##testname cls_##testname##_instance;
321
322 /*
323  * A "fixture" is a subclass of JSAPITest that holds common definitions for a
324  * set of tests. Each test that wants to use the fixture should use
325  * BEGIN_FIXTURE_TEST and END_FIXTURE_TEST, just as one would use BEGIN_TEST and
326  * END_TEST, but include the fixture class as the first argument. The fixture
327  * class's declarations are then in scope for the test bodies.
328  */
329
330 #define BEGIN_FIXTURE_TEST(fixture, testname)                           \
331     class cls_##testname : public fixture {                             \
332       public:                                                           \
333         virtual const char * name() { return #testname; }               \
334         virtual bool run()
335
336 #define END_FIXTURE_TEST(fixture, testname)                             \
337     };                                                                  \
338     static cls_##testname cls_##testname##_instance;
339
340 /*
341  * A class for creating and managing one temporary file.
342  * 
343  * We could use the ISO C temporary file functions here, but those try to
344  * create files in the root directory on Windows, which fails for users
345  * without Administrator privileges.
346  */
347 class TempFile {
348     const char *name;
349     FILE *stream;
350
351   public:
352     TempFile() : name(), stream() { }
353     ~TempFile() {
354         if (stream)
355             close();
356         if (name)
357             remove();
358     }
359
360     /*
361      * Return a stream for a temporary file named |fileName|. Infallible.
362      * Use only once per TempFile instance. If the file is not explicitly
363      * closed and deleted via the member functions below, this object's
364      * destructor will clean them up.
365      */
366     FILE *open(const char *fileName)
367     {
368         stream = fopen(fileName, "wb+");
369         if (!stream) {
370             fprintf(stderr, "error opening temporary file '%s': %s\n",
371                     fileName, strerror(errno));
372             exit(1);
373         }            
374         name = fileName;
375         return stream;
376     }
377
378     /* Close the temporary file's stream. */
379     void close() {
380         if (fclose(stream) == EOF) {
381             fprintf(stderr, "error closing temporary file '%s': %s\n",
382                     name, strerror(errno));
383             exit(1);
384         }
385         stream = NULL;
386     }
387
388     /* Delete the temporary file. */
389     void remove() {
390         if (::remove(name) != 0) {
391             fprintf(stderr, "error deleting temporary file '%s': %s\n",
392                     name, strerror(errno));
393             exit(1);
394         }
395         name = NULL;
396     }
397 };