1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsapi-tests/tests.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,418 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jsapi_tests_tests_h 1.11 +#define jsapi_tests_tests_h 1.12 + 1.13 +#include <errno.h> 1.14 +#include <stdio.h> 1.15 +#include <stdlib.h> 1.16 +#include <string.h> 1.17 + 1.18 +#include "jsalloc.h" 1.19 +#include "jscntxt.h" 1.20 +#include "jsgc.h" 1.21 + 1.22 +#include "js/Vector.h" 1.23 + 1.24 +/* Note: Aborts on OOM. */ 1.25 +class JSAPITestString { 1.26 + js::Vector<char, 0, js::SystemAllocPolicy> chars; 1.27 + public: 1.28 + JSAPITestString() {} 1.29 + JSAPITestString(const char *s) { *this += s; } 1.30 + JSAPITestString(const JSAPITestString &s) { *this += s; } 1.31 + 1.32 + const char *begin() const { return chars.begin(); } 1.33 + const char *end() const { return chars.end(); } 1.34 + size_t length() const { return chars.length(); } 1.35 + 1.36 + JSAPITestString & operator +=(const char *s) { 1.37 + if (!chars.append(s, strlen(s))) 1.38 + abort(); 1.39 + return *this; 1.40 + } 1.41 + 1.42 + JSAPITestString & operator +=(const JSAPITestString &s) { 1.43 + if (!chars.append(s.begin(), s.length())) 1.44 + abort(); 1.45 + return *this; 1.46 + } 1.47 +}; 1.48 + 1.49 +inline JSAPITestString operator+(JSAPITestString a, const char *b) { return a += b; } 1.50 +inline JSAPITestString operator+(JSAPITestString a, const JSAPITestString &b) { return a += b; } 1.51 + 1.52 +class JSAPITest 1.53 +{ 1.54 + public: 1.55 + static JSAPITest *list; 1.56 + JSAPITest *next; 1.57 + 1.58 + JSRuntime *rt; 1.59 + JSContext *cx; 1.60 + JS::Heap<JSObject *> global; 1.61 + bool knownFail; 1.62 + JSAPITestString msgs; 1.63 + JSCompartment *oldCompartment; 1.64 + 1.65 + JSAPITest() : rt(nullptr), cx(nullptr), global(nullptr), 1.66 + knownFail(false), oldCompartment(nullptr) { 1.67 + next = list; 1.68 + list = this; 1.69 + } 1.70 + 1.71 + virtual ~JSAPITest() { uninit(); } 1.72 + 1.73 + virtual bool init(); 1.74 + 1.75 + virtual void uninit() { 1.76 + if (oldCompartment) { 1.77 + JS_LeaveCompartment(cx, oldCompartment); 1.78 + oldCompartment = nullptr; 1.79 + } 1.80 + global = nullptr; 1.81 + if (cx) { 1.82 + JS::RemoveObjectRoot(cx, &global); 1.83 + JS_LeaveCompartment(cx, nullptr); 1.84 + JS_EndRequest(cx); 1.85 + JS_DestroyContext(cx); 1.86 + cx = nullptr; 1.87 + } 1.88 + if (rt) { 1.89 + destroyRuntime(); 1.90 + rt = nullptr; 1.91 + } 1.92 + } 1.93 + 1.94 + virtual const char * name() = 0; 1.95 + virtual bool run(JS::HandleObject global) = 0; 1.96 + 1.97 +#define EXEC(s) do { if (!exec(s, __FILE__, __LINE__)) return false; } while (false) 1.98 + 1.99 + bool exec(const char *bytes, const char *filename, int lineno); 1.100 + 1.101 +#define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false) 1.102 + 1.103 + bool evaluate(const char *bytes, const char *filename, int lineno, JS::MutableHandleValue vp); 1.104 + 1.105 + JSAPITestString jsvalToSource(JS::HandleValue v) { 1.106 + JSString *str = JS_ValueToSource(cx, v); 1.107 + if (str) { 1.108 + JSAutoByteString bytes(cx, str); 1.109 + if (!!bytes) 1.110 + return JSAPITestString(bytes.ptr()); 1.111 + } 1.112 + JS_ClearPendingException(cx); 1.113 + return JSAPITestString("<<error converting value to string>>"); 1.114 + } 1.115 + 1.116 + JSAPITestString toSource(long v) { 1.117 + char buf[40]; 1.118 + sprintf(buf, "%ld", v); 1.119 + return JSAPITestString(buf); 1.120 + } 1.121 + 1.122 + JSAPITestString toSource(unsigned long v) { 1.123 + char buf[40]; 1.124 + sprintf(buf, "%lu", v); 1.125 + return JSAPITestString(buf); 1.126 + } 1.127 + 1.128 + JSAPITestString toSource(long long v) { 1.129 + char buf[40]; 1.130 + sprintf(buf, "%lld", v); 1.131 + return JSAPITestString(buf); 1.132 + } 1.133 + 1.134 + JSAPITestString toSource(unsigned long long v) { 1.135 + char buf[40]; 1.136 + sprintf(buf, "%llu", v); 1.137 + return JSAPITestString(buf); 1.138 + } 1.139 + 1.140 + JSAPITestString toSource(unsigned int v) { 1.141 + return toSource((unsigned long)v); 1.142 + } 1.143 + 1.144 + JSAPITestString toSource(int v) { 1.145 + return toSource((long)v); 1.146 + } 1.147 + 1.148 + JSAPITestString toSource(bool v) { 1.149 + return JSAPITestString(v ? "true" : "false"); 1.150 + } 1.151 + 1.152 + JSAPITestString toSource(JSAtom *v) { 1.153 + JS::RootedValue val(cx, JS::StringValue((JSString *)v)); 1.154 + return jsvalToSource(val); 1.155 + } 1.156 + 1.157 + JSAPITestString toSource(JSVersion v) { 1.158 + return JSAPITestString(JS_VersionToString(v)); 1.159 + } 1.160 + 1.161 + template<typename T> 1.162 + bool checkEqual(const T &actual, const T &expected, 1.163 + const char *actualExpr, const char *expectedExpr, 1.164 + const char *filename, int lineno) { 1.165 + return (actual == expected) || 1.166 + fail(JSAPITestString("CHECK_EQUAL failed: expected (") + 1.167 + expectedExpr + ") = " + toSource(expected) + 1.168 + ", got (" + actualExpr + ") = " + toSource(actual), filename, lineno); 1.169 + } 1.170 + 1.171 + // There are many cases where the static types of 'actual' and 'expected' 1.172 + // are not identical, and C++ is understandably cautious about automatic 1.173 + // coercions. So catch those cases and forcibly coerce, then use the 1.174 + // identical-type specialization. This may do bad things if the types are 1.175 + // actually *not* compatible. 1.176 + template<typename T, typename U> 1.177 + bool checkEqual(const T &actual, const U &expected, 1.178 + const char *actualExpr, const char *expectedExpr, 1.179 + const char *filename, int lineno) { 1.180 + return checkEqual(U(actual), expected, actualExpr, expectedExpr, filename, lineno); 1.181 + } 1.182 + 1.183 +#define CHECK_EQUAL(actual, expected) \ 1.184 + do { \ 1.185 + if (!checkEqual(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ 1.186 + return false; \ 1.187 + } while (false) 1.188 + 1.189 + bool checkSame(jsval actualArg, jsval expectedArg, 1.190 + const char *actualExpr, const char *expectedExpr, 1.191 + const char *filename, int lineno) { 1.192 + bool same; 1.193 + JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg); 1.194 + return (JS_SameValue(cx, actual, expected, &same) && same) || 1.195 + fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") + 1.196 + actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " + 1.197 + jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno); 1.198 + } 1.199 + 1.200 +#define CHECK_SAME(actual, expected) \ 1.201 + do { \ 1.202 + if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ 1.203 + return false; \ 1.204 + } while (false) 1.205 + 1.206 +#define CHECK(expr) \ 1.207 + do { \ 1.208 + if (!(expr)) \ 1.209 + return fail("CHECK failed: " #expr, __FILE__, __LINE__); \ 1.210 + } while (false) 1.211 + 1.212 + bool fail(JSAPITestString msg = JSAPITestString(), const char *filename = "-", int lineno = 0) { 1.213 + if (JS_IsExceptionPending(cx)) { 1.214 + js::gc::AutoSuppressGC gcoff(cx); 1.215 + JS::RootedValue v(cx); 1.216 + JS_GetPendingException(cx, &v); 1.217 + JS_ClearPendingException(cx); 1.218 + JSString *s = JS::ToString(cx, v); 1.219 + if (s) { 1.220 + JSAutoByteString bytes(cx, s); 1.221 + if (!!bytes) 1.222 + msg += bytes.ptr(); 1.223 + } 1.224 + } 1.225 + fprintf(stderr, "%s:%d:%.*s\n", filename, lineno, (int) msg.length(), msg.begin()); 1.226 + msgs += msg; 1.227 + return false; 1.228 + } 1.229 + 1.230 + JSAPITestString messages() const { return msgs; } 1.231 + 1.232 + static const JSClass * basicGlobalClass() { 1.233 + static const JSClass c = { 1.234 + "global", JSCLASS_GLOBAL_FLAGS, 1.235 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.236 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, 1.237 + nullptr, nullptr, nullptr, 1.238 + JS_GlobalObjectTraceHook 1.239 + }; 1.240 + return &c; 1.241 + } 1.242 + 1.243 + protected: 1.244 + static bool 1.245 + print(JSContext *cx, unsigned argc, jsval *vp) 1.246 + { 1.247 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.248 + 1.249 + for (unsigned i = 0; i < args.length(); i++) { 1.250 + JSString *str = JS::ToString(cx, args[i]); 1.251 + if (!str) 1.252 + return false; 1.253 + char *bytes = JS_EncodeString(cx, str); 1.254 + if (!bytes) 1.255 + return false; 1.256 + printf("%s%s", i ? " " : "", bytes); 1.257 + JS_free(cx, bytes); 1.258 + } 1.259 + 1.260 + putchar('\n'); 1.261 + fflush(stdout); 1.262 + args.rval().setUndefined(); 1.263 + return true; 1.264 + } 1.265 + 1.266 + bool definePrint(); 1.267 + 1.268 + static void setNativeStackQuota(JSRuntime *rt) 1.269 + { 1.270 + const size_t MAX_STACK_SIZE = 1.271 +/* Assume we can't use more than 5e5 bytes of C stack by default. */ 1.272 +#if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC) 1.273 + /* 1.274 + * Sun compiler uses a larger stack space for js::Interpret() with 1.275 + * debug. Use a bigger gMaxStackSize to make "make check" happy. 1.276 + */ 1.277 + 5000000 1.278 +#else 1.279 + 500000 1.280 +#endif 1.281 + ; 1.282 + 1.283 + JS_SetNativeStackQuota(rt, MAX_STACK_SIZE); 1.284 + } 1.285 + 1.286 + virtual JSRuntime * createRuntime() { 1.287 + JSRuntime *rt = JS_NewRuntime(8L * 1024 * 1024, JS_USE_HELPER_THREADS); 1.288 + if (!rt) 1.289 + return nullptr; 1.290 + setNativeStackQuota(rt); 1.291 + return rt; 1.292 + } 1.293 + 1.294 + virtual void destroyRuntime() { 1.295 + JS_ASSERT(!cx); 1.296 + JS_ASSERT(rt); 1.297 + JS_DestroyRuntime(rt); 1.298 + } 1.299 + 1.300 + static void reportError(JSContext *cx, const char *message, JSErrorReport *report) { 1.301 + fprintf(stderr, "%s:%u:%s\n", 1.302 + report->filename ? report->filename : "<no filename>", 1.303 + (unsigned int) report->lineno, 1.304 + message); 1.305 + } 1.306 + 1.307 + virtual JSContext * createContext() { 1.308 + JSContext *cx = JS_NewContext(rt, 8192); 1.309 + if (!cx) 1.310 + return nullptr; 1.311 + JS::ContextOptionsRef(cx).setVarObjFix(true); 1.312 + JS_SetErrorReporter(cx, &reportError); 1.313 + return cx; 1.314 + } 1.315 + 1.316 + virtual const JSClass * getGlobalClass() { 1.317 + return basicGlobalClass(); 1.318 + } 1.319 + 1.320 + virtual JSObject * createGlobal(JSPrincipals *principals = nullptr); 1.321 +}; 1.322 + 1.323 +#define BEGIN_TEST(testname) \ 1.324 + class cls_##testname : public JSAPITest { \ 1.325 + public: \ 1.326 + virtual const char * name() { return #testname; } \ 1.327 + virtual bool run(JS::HandleObject global) 1.328 + 1.329 +#define END_TEST(testname) \ 1.330 + }; \ 1.331 + static cls_##testname cls_##testname##_instance; 1.332 + 1.333 +/* 1.334 + * A "fixture" is a subclass of JSAPITest that holds common definitions for a 1.335 + * set of tests. Each test that wants to use the fixture should use 1.336 + * BEGIN_FIXTURE_TEST and END_FIXTURE_TEST, just as one would use BEGIN_TEST and 1.337 + * END_TEST, but include the fixture class as the first argument. The fixture 1.338 + * class's declarations are then in scope for the test bodies. 1.339 + */ 1.340 + 1.341 +#define BEGIN_FIXTURE_TEST(fixture, testname) \ 1.342 + class cls_##testname : public fixture { \ 1.343 + public: \ 1.344 + virtual const char * name() { return #testname; } \ 1.345 + virtual bool run(JS::HandleObject global) 1.346 + 1.347 +#define END_FIXTURE_TEST(fixture, testname) \ 1.348 + }; \ 1.349 + static cls_##testname cls_##testname##_instance; 1.350 + 1.351 +/* 1.352 + * A class for creating and managing one temporary file. 1.353 + * 1.354 + * We could use the ISO C temporary file functions here, but those try to 1.355 + * create files in the root directory on Windows, which fails for users 1.356 + * without Administrator privileges. 1.357 + */ 1.358 +class TempFile { 1.359 + const char *name; 1.360 + FILE *stream; 1.361 + 1.362 + public: 1.363 + TempFile() : name(), stream() { } 1.364 + ~TempFile() { 1.365 + if (stream) 1.366 + close(); 1.367 + if (name) 1.368 + remove(); 1.369 + } 1.370 + 1.371 + /* 1.372 + * Return a stream for a temporary file named |fileName|. Infallible. 1.373 + * Use only once per TempFile instance. If the file is not explicitly 1.374 + * closed and deleted via the member functions below, this object's 1.375 + * destructor will clean them up. 1.376 + */ 1.377 + FILE *open(const char *fileName) 1.378 + { 1.379 + stream = fopen(fileName, "wb+"); 1.380 + if (!stream) { 1.381 + fprintf(stderr, "error opening temporary file '%s': %s\n", 1.382 + fileName, strerror(errno)); 1.383 + exit(1); 1.384 + } 1.385 + name = fileName; 1.386 + return stream; 1.387 + } 1.388 + 1.389 + /* Close the temporary file's stream. */ 1.390 + void close() { 1.391 + if (fclose(stream) == EOF) { 1.392 + fprintf(stderr, "error closing temporary file '%s': %s\n", 1.393 + name, strerror(errno)); 1.394 + exit(1); 1.395 + } 1.396 + stream = nullptr; 1.397 + } 1.398 + 1.399 + /* Delete the temporary file. */ 1.400 + void remove() { 1.401 + if (::remove(name) != 0) { 1.402 + fprintf(stderr, "error deleting temporary file '%s': %s\n", 1.403 + name, strerror(errno)); 1.404 + exit(1); 1.405 + } 1.406 + name = nullptr; 1.407 + } 1.408 +}; 1.409 + 1.410 +// Just a wrapper around JSPrincipals that allows static construction. 1.411 +class TestJSPrincipals : public JSPrincipals 1.412 +{ 1.413 + public: 1.414 + TestJSPrincipals(int rc = 0) 1.415 + : JSPrincipals() 1.416 + { 1.417 + refcount = rc; 1.418 + } 1.419 +}; 1.420 + 1.421 +#endif /* jsapi_tests_tests_h */