js/src/jsapi-tests/tests.h

changeset 0
6474c204b198
     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 */

mercurial