~ubuntu-branches/ubuntu/wily/juju-mongodb/wily-proposed

« back to all changes in this revision

Viewing changes to .pc/0005-disable-jstests.patch/src/mongo/dbtests/jstests.cpp

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-07 21:51:41 UTC
  • Revision ID: package-import@ubuntu.com-20140107215141-cwf4ad7nncxawj6h
Tags: 2.4.8-0ubuntu6
* Switch back to running standard smoke test suite, minus jstests:
  - d/rules: Use smoke target for test execution.
  - d/patches/0005-disable-jstests.patch: Disable jstests suite.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// javajstests.cpp
 
2
//
 
3
 
 
4
/**
 
5
 *    Copyright (C) 2009 10gen Inc.
 
6
 *
 
7
 *    This program is free software: you can redistribute it and/or  modify
 
8
 *    it under the terms of the GNU Affero General Public License, version 3,
 
9
 *    as published by the Free Software Foundation.
 
10
 *
 
11
 *    This program is distributed in the hope that it will be useful,
 
12
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *    GNU Affero General Public License for more details.
 
15
 *
 
16
 *    You should have received a copy of the GNU Affero General Public License
 
17
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include "pch.h"
 
21
 
 
22
#include "mongo/db/instance.h"
 
23
#include "mongo/scripting/engine.h"
 
24
#include "mongo/util/timer.h"
 
25
#include "mongo/dbtests/dbtests.h"
 
26
#include "mongo/db/json.h"
 
27
 
 
28
namespace mongo {
 
29
    bool dbEval(const string& dbName , BSONObj& cmd, BSONObjBuilder& result, string& errmsg);
 
30
} // namespace mongo
 
31
 
 
32
namespace JSTests {
 
33
 
 
34
    class BuiltinTests {
 
35
    public:
 
36
        void run() {
 
37
            // Run any tests included with the scripting engine
 
38
            globalScriptEngine->runTest();
 
39
        }
 
40
    };
 
41
 
 
42
    class BasicScope {
 
43
    public:
 
44
        void run() {
 
45
            scoped_ptr<Scope> s;
 
46
            s.reset( globalScriptEngine->newScope() );
 
47
 
 
48
            s->setNumber( "x" , 5 );
 
49
            ASSERT( 5 == s->getNumber( "x" ) );
 
50
 
 
51
            s->setNumber( "x" , 1.67 );
 
52
            ASSERT( 1.67 == s->getNumber( "x" ) );
 
53
 
 
54
            s->setString( "s" , "eliot was here" );
 
55
            ASSERT( "eliot was here" == s->getString( "s" ) );
 
56
 
 
57
            s->setBoolean( "b" , true );
 
58
            ASSERT( s->getBoolean( "b" ) );
 
59
 
 
60
            s->setBoolean( "b" , false );
 
61
            ASSERT( ! s->getBoolean( "b" ) );
 
62
        }
 
63
    };
 
64
 
 
65
    class ResetScope {
 
66
    public:
 
67
        void run() {
 
68
            /* Currently reset does not clear data in v8 or spidermonkey scopes.  See SECURITY-10
 
69
            auto_ptr<Scope> s;
 
70
            s.reset( globalScriptEngine->newScope() );
 
71
 
 
72
            s->setBoolean( "x" , true );
 
73
            ASSERT( s->getBoolean( "x" ) );
 
74
 
 
75
            s->reset();
 
76
            ASSERT( !s->getBoolean( "x" ) );
 
77
            */
 
78
        }
 
79
    };
 
80
 
 
81
    class FalseTests {
 
82
    public:
 
83
        void run() {
 
84
            // Test falsy javascript values
 
85
            scoped_ptr<Scope> s;
 
86
            s.reset( globalScriptEngine->newScope() );
 
87
 
 
88
            ASSERT( ! s->getBoolean( "notSet" ) );
 
89
 
 
90
            s->setString( "emptyString" , "" );
 
91
            ASSERT( ! s->getBoolean( "emptyString" ) );
 
92
 
 
93
            s->setNumber( "notANumberVal" , std::numeric_limits<double>::quiet_NaN());
 
94
            ASSERT( ! s->getBoolean( "notANumberVal" ) );
 
95
 
 
96
            s->setElement( "nullVal" , BSONObjBuilder().appendNull("null").obj().getField("null") );
 
97
            ASSERT( ! s->getBoolean( "nullVal" ) );
 
98
 
 
99
            s->setNumber( "zeroVal" , 0 );
 
100
            ASSERT( ! s->getBoolean( "zeroVal" ) );
 
101
        }
 
102
    };
 
103
 
 
104
    class SimpleFunctions {
 
105
    public:
 
106
        void run() {
 
107
            Scope * s = globalScriptEngine->newScope();
 
108
 
 
109
            s->invoke( "x=5;" , 0, 0 );
 
110
            ASSERT( 5 == s->getNumber( "x" ) );
 
111
 
 
112
            s->invoke( "return 17;" , 0, 0 );
 
113
            ASSERT( 17 == s->getNumber( "__returnValue" ) );
 
114
 
 
115
            s->invoke( "function(){ return 17; }" , 0, 0 );
 
116
            ASSERT( 17 == s->getNumber( "__returnValue" ) );
 
117
 
 
118
            s->setNumber( "x" , 1.76 );
 
119
            s->invoke( "return x == 1.76; " , 0, 0 );
 
120
            ASSERT( s->getBoolean( "__returnValue" ) );
 
121
 
 
122
            s->setNumber( "x" , 1.76 );
 
123
            s->invoke( "return x == 1.79; " , 0, 0 );
 
124
            ASSERT( ! s->getBoolean( "__returnValue" ) );
 
125
 
 
126
            BSONObj obj = BSON( "" << 11.0 );
 
127
            s->invoke( "function( z ){ return 5 + z; }" , &obj, 0 );
 
128
            ASSERT_EQUALS( 16 , s->getNumber( "__returnValue" ) );
 
129
 
 
130
            delete s;
 
131
        }
 
132
    };
 
133
 
 
134
    /** Installs a tee for auditing log messages, including those logged with tlog(). */
 
135
    class LogRecordingScope {
 
136
    public:
 
137
        LogRecordingScope() :
 
138
            _oldTLogLevel( tlogLevel ) {
 
139
            tlogLevel = 0;
 
140
            Logstream::get().addGlobalTee( &_tee );
 
141
        }
 
142
        ~LogRecordingScope() {
 
143
            Logstream::get().removeGlobalTee( &_tee );
 
144
            tlogLevel = _oldTLogLevel;
 
145
        }
 
146
        /** @return most recent log entry. */
 
147
        bool logged() const { return _tee.logged(); }
 
148
    private:
 
149
        class Tee : public mongo::Tee {
 
150
        public:
 
151
            Tee() :
 
152
                _logged() {
 
153
            }
 
154
            virtual void write( LogLevel level, const string &str ) { _logged = true; }
 
155
            bool logged() const { return _logged; }
 
156
        private:
 
157
            bool _logged;
 
158
        };
 
159
        int _oldTLogLevel;
 
160
        Tee _tee;
 
161
    };
 
162
    
 
163
    /** Error logging in Scope::exec(). */
 
164
    class ExecLogError {
 
165
    public:
 
166
        void run() {
 
167
            Scope *scope = globalScriptEngine->newScope();
 
168
 
 
169
            // No error is logged when reportError == false.
 
170
            ASSERT( !scope->exec( "notAFunction()", "foo", false, false, false ) );
 
171
            ASSERT( !_logger.logged() );
 
172
            
 
173
            // No error is logged for a valid statement.
 
174
            ASSERT( scope->exec( "validStatement = true", "foo", false, true, false ) );
 
175
            ASSERT( !_logger.logged() );
 
176
 
 
177
            // An error is logged for an invalid statement when reportError == true.
 
178
            ASSERT( !scope->exec( "notAFunction()", "foo", false, true, false ) );
 
179
            ASSERT( _logger.logged() );
 
180
        }
 
181
    private:
 
182
        LogRecordingScope _logger;
 
183
    };
 
184
    
 
185
    /** Error logging in Scope::invoke(). */
 
186
    class InvokeLogError {
 
187
    public:
 
188
        void run() {
 
189
            Scope *scope = globalScriptEngine->newScope();
 
190
 
 
191
            // No error is logged for a valid statement.
 
192
            ASSERT_EQUALS( 0, scope->invoke( "validStatement = true", 0, 0 ) );
 
193
            ASSERT( !_logger.logged() );
 
194
 
 
195
            // An error is logged for an invalid statement.
 
196
            try {
 
197
                scope->invoke( "notAFunction()", 0, 0 );
 
198
            }
 
199
            catch(const DBException&) {
 
200
                // ignore the exception; just test that we logged something
 
201
            }
 
202
            ASSERT( _logger.logged() );
 
203
        }
 
204
    private:
 
205
        LogRecordingScope _logger;
 
206
    };
 
207
 
 
208
    class ObjectMapping {
 
209
    public:
 
210
        void run() {
 
211
            Scope * s = globalScriptEngine->newScope();
 
212
 
 
213
            BSONObj o = BSON( "x" << 17.0 << "y" << "eliot" << "z" << "sara" );
 
214
            s->setObject( "blah" , o );
 
215
 
 
216
            s->invoke( "return blah.x;" , 0, 0 );
 
217
            ASSERT_EQUALS( 17 , s->getNumber( "__returnValue" ) );
 
218
            s->invoke( "return blah.y;" , 0, 0 );
 
219
            ASSERT_EQUALS( "eliot" , s->getString( "__returnValue" ) );
 
220
 
 
221
            s->invoke( "return this.z;" , 0, &o );
 
222
            ASSERT_EQUALS( "sara" , s->getString( "__returnValue" ) );
 
223
 
 
224
            s->invoke( "return this.z == 'sara';" , 0, &o );
 
225
            ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) );
 
226
 
 
227
            s->invoke( "this.z == 'sara';" , 0, &o );
 
228
            ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) );
 
229
 
 
230
            s->invoke( "this.z == 'asara';" , 0, &o );
 
231
            ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) );
 
232
 
 
233
            s->invoke( "return this.x == 17;" , 0, &o );
 
234
            ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) );
 
235
 
 
236
            s->invoke( "return this.x == 18;" , 0, &o );
 
237
            ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) );
 
238
 
 
239
            s->invoke( "function(){ return this.x == 17; }" , 0, &o );
 
240
            ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) );
 
241
 
 
242
            s->invoke( "function(){ return this.x == 18; }" , 0, &o );
 
243
            ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) );
 
244
 
 
245
            s->invoke( "function (){ return this.x == 17; }" , 0, &o );
 
246
            ASSERT_EQUALS( true , s->getBoolean( "__returnValue" ) );
 
247
 
 
248
            s->invoke( "function z(){ return this.x == 18; }" , 0, &o );
 
249
            ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) );
 
250
 
 
251
            s->invoke( "function (){ this.x == 17; }" , 0, &o );
 
252
            ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) );
 
253
 
 
254
            s->invoke( "function z(){ this.x == 18; }" , 0, &o );
 
255
            ASSERT_EQUALS( false , s->getBoolean( "__returnValue" ) );
 
256
 
 
257
            s->invoke( "x = 5; for( ; x <10; x++){ a = 1; }" , 0, &o );
 
258
            ASSERT_EQUALS( 10 , s->getNumber( "x" ) );
 
259
 
 
260
            delete s;
 
261
        }
 
262
    };
 
263
 
 
264
    class ObjectDecoding {
 
265
    public:
 
266
        void run() {
 
267
            Scope * s = globalScriptEngine->newScope();
 
268
 
 
269
            s->invoke( "z = { num : 1 };" , 0, 0 );
 
270
            BSONObj out = s->getObject( "z" );
 
271
            ASSERT_EQUALS( 1 , out["num"].number() );
 
272
            ASSERT_EQUALS( 1 , out.nFields() );
 
273
 
 
274
            s->invoke( "z = { x : 'eliot' };" , 0, 0 );
 
275
            out = s->getObject( "z" );
 
276
            ASSERT_EQUALS( (string)"eliot" , out["x"].valuestr() );
 
277
            ASSERT_EQUALS( 1 , out.nFields() );
 
278
 
 
279
            BSONObj o = BSON( "x" << 17 );
 
280
            s->setObject( "blah" , o );
 
281
            out = s->getObject( "blah" );
 
282
            ASSERT_EQUALS( 17 , out["x"].number() );
 
283
 
 
284
            delete s;
 
285
        }
 
286
    };
 
287
 
 
288
    class JSOIDTests {
 
289
    public:
 
290
        void run() {
 
291
#ifdef MOZJS
 
292
            Scope * s = globalScriptEngine->newScope();
 
293
 
 
294
            s->localConnect( "blah" );
 
295
 
 
296
            s->invoke( "z = { _id : new ObjectId() , a : 123 };" , 0, 0 );
 
297
            BSONObj out = s->getObject( "z" );
 
298
            ASSERT_EQUALS( 123 , out["a"].number() );
 
299
            ASSERT_EQUALS( jstOID , out["_id"].type() );
 
300
 
 
301
            OID save = out["_id"].__oid();
 
302
 
 
303
            s->setObject( "a" , out );
 
304
 
 
305
            s->invoke( "y = { _id : a._id , a : 124 };" , 0, 0 );
 
306
            out = s->getObject( "y" );
 
307
            ASSERT_EQUALS( 124 , out["a"].number() );
 
308
            ASSERT_EQUALS( jstOID , out["_id"].type() );
 
309
            ASSERT_EQUALS( out["_id"].__oid().str() , save.str() );
 
310
 
 
311
            s->invoke( "y = { _id : new ObjectId( a._id ) , a : 125 };" , 0, 0 );
 
312
            out = s->getObject( "y" );
 
313
            ASSERT_EQUALS( 125 , out["a"].number() );
 
314
            ASSERT_EQUALS( jstOID , out["_id"].type() );
 
315
            ASSERT_EQUALS( out["_id"].__oid().str() , save.str() );
 
316
 
 
317
            delete s;
 
318
#endif
 
319
        }
 
320
    };
 
321
 
 
322
    class SetImplicit {
 
323
    public:
 
324
        void run() {
 
325
            Scope *s = globalScriptEngine->newScope();
 
326
 
 
327
            BSONObj o = BSON( "foo" << "bar" );
 
328
            s->setObject( "a.b", o );
 
329
            ASSERT( s->getObject( "a" ).isEmpty() );
 
330
 
 
331
            BSONObj o2 = BSONObj();
 
332
            s->setObject( "a", o2 );
 
333
            s->setObject( "a.b", o );
 
334
            ASSERT( s->getObject( "a" ).isEmpty() );
 
335
 
 
336
            o2 = fromjson( "{b:{}}" );
 
337
            s->setObject( "a", o2 );
 
338
            s->setObject( "a.b", o );
 
339
            ASSERT( !s->getObject( "a" ).isEmpty() );
 
340
        }
 
341
    };
 
342
 
 
343
    class ObjectModReadonlyTests {
 
344
    public:
 
345
        void run() {
 
346
            Scope * s = globalScriptEngine->newScope();
 
347
 
 
348
            BSONObj o = BSON( "x" << 17 << "y" << "eliot" << "z" << "sara" << "zz" << BSONObj() );
 
349
            s->setObject( "blah" , o , true );
 
350
 
 
351
            s->invoke( "blah.y = 'e'", 0, 0 );
 
352
            BSONObj out = s->getObject( "blah" );
 
353
            ASSERT( strlen( out["y"].valuestr() ) > 1 );
 
354
 
 
355
            s->invoke( "blah.a = 19;" , 0, 0 );
 
356
            out = s->getObject( "blah" );
 
357
            ASSERT( out["a"].eoo() );
 
358
 
 
359
            s->invoke( "blah.zz.a = 19;" , 0, 0 );
 
360
            out = s->getObject( "blah" );
 
361
            ASSERT( out["zz"].embeddedObject()["a"].eoo() );
 
362
 
 
363
            s->setObject( "blah.zz", BSON( "a" << 19 ) );
 
364
            out = s->getObject( "blah" );
 
365
            ASSERT( out["zz"].embeddedObject()["a"].eoo() );
 
366
 
 
367
            s->invoke( "delete blah['x']" , 0, 0 );
 
368
            out = s->getObject( "blah" );
 
369
            ASSERT( !out["x"].eoo() );
 
370
 
 
371
            // read-only object itself can be overwritten
 
372
            s->invoke( "blah = {}", 0, 0 );
 
373
            out = s->getObject( "blah" );
 
374
            ASSERT( out.isEmpty() );
 
375
 
 
376
            // test array - can't implement this in v8
 
377
//            o = fromjson( "{a:[1,2,3]}" );
 
378
//            s->setObject( "blah", o, true );
 
379
//            out = s->getObject( "blah" );
 
380
//            s->invoke( "blah.a[ 0 ] = 4;", BSONObj() );
 
381
//            s->invoke( "delete blah['a'][ 2 ];", BSONObj() );
 
382
//            out = s->getObject( "blah" );
 
383
//            ASSERT_EQUALS( 1.0, out[ "a" ].embeddedObject()[ 0 ].number() );
 
384
//            ASSERT_EQUALS( 3.0, out[ "a" ].embeddedObject()[ 2 ].number() );
 
385
 
 
386
            delete s;
 
387
        }
 
388
    };
 
389
 
 
390
    class OtherJSTypes {
 
391
    public:
 
392
        void run() {
 
393
            Scope * s = globalScriptEngine->newScope();
 
394
 
 
395
            {
 
396
                // date
 
397
                BSONObj o;
 
398
                {
 
399
                    BSONObjBuilder b;
 
400
                    b.appendDate( "d" , 123456789 );
 
401
                    o = b.obj();
 
402
                }
 
403
                s->setObject( "x" , o );
 
404
 
 
405
                s->invoke( "return x.d.getTime() != 12;" , 0, 0 );
 
406
                ASSERT_EQUALS( true, s->getBoolean( "__returnValue" ) );
 
407
 
 
408
                s->invoke( "z = x.d.getTime();" , 0, 0 );
 
409
                ASSERT_EQUALS( 123456789 , s->getNumber( "z" ) );
 
410
 
 
411
                s->invoke( "z = { z : x.d }" , 0, 0 );
 
412
                BSONObj out = s->getObject( "z" );
 
413
                ASSERT( out["z"].type() == Date );
 
414
            }
 
415
 
 
416
            {
 
417
                // regex
 
418
                BSONObj o;
 
419
                {
 
420
                    BSONObjBuilder b;
 
421
                    b.appendRegex( "r" , "^a" , "i" );
 
422
                    o = b.obj();
 
423
                }
 
424
                s->setObject( "x" , o );
 
425
 
 
426
                s->invoke( "z = x.r.test( 'b' );" , 0, 0 );
 
427
                ASSERT_EQUALS( false , s->getBoolean( "z" ) );
 
428
 
 
429
                s->invoke( "z = x.r.test( 'a' );" , 0, 0 );
 
430
                ASSERT_EQUALS( true , s->getBoolean( "z" ) );
 
431
 
 
432
                s->invoke( "z = x.r.test( 'ba' );" , 0, 0 );
 
433
                ASSERT_EQUALS( false , s->getBoolean( "z" ) );
 
434
 
 
435
                s->invoke( "z = { a : x.r };" , 0, 0 );
 
436
 
 
437
                BSONObj out = s->getObject("z");
 
438
                ASSERT_EQUALS( (string)"^a" , out["a"].regex() );
 
439
                ASSERT_EQUALS( (string)"i" , out["a"].regexFlags() );
 
440
 
 
441
                // This regex used to cause a segfault because x isn't a valid flag for a js RegExp.
 
442
                // Now it throws a JS exception.
 
443
                BSONObj invalidRegex = BSON_ARRAY(BSON("regex" << BSONRegEx("asdf", "x")));
 
444
                const char* code = "function (obj) {"
 
445
                                   "    var threw = false;"
 
446
                                   "    try {"
 
447
                                   "        obj.regex;" // should throw
 
448
                                   "    } catch(e) {"
 
449
                                   "         threw = true;"
 
450
                                   "    }"
 
451
                                   "    assert(threw);"
 
452
                                   "}";
 
453
                ASSERT_EQUALS(s->invoke(code, &invalidRegex, NULL), 0);
 
454
            }
 
455
 
 
456
            // array
 
457
            {
 
458
                BSONObj o = fromjson( "{r:[1,2,3]}" );
 
459
                s->setObject( "x", o, false );
 
460
                BSONObj out = s->getObject( "x" );
 
461
                ASSERT_EQUALS( Array, out.firstElement().type() );
 
462
 
 
463
                s->setObject( "x", o, true );
 
464
                out = s->getObject( "x" );
 
465
                ASSERT_EQUALS( Array, out.firstElement().type() );
 
466
            }
 
467
 
 
468
            // symbol
 
469
            {
 
470
                // test mutable object with symbol type
 
471
                BSONObjBuilder builder;
 
472
                builder.appendSymbol("sym", "value");
 
473
                BSONObj in = builder.done();
 
474
                s->setObject( "x", in, false );
 
475
                BSONObj out = s->getObject( "x" );
 
476
                ASSERT_EQUALS( Symbol, out.firstElement().type() );
 
477
 
 
478
                // readonly
 
479
                s->setObject( "x", in, true );
 
480
                out = s->getObject( "x" );
 
481
                ASSERT_EQUALS( Symbol, out.firstElement().type() );
 
482
            }
 
483
 
 
484
            delete s;
 
485
        }
 
486
    };
 
487
 
 
488
    class SpecialDBTypes {
 
489
    public:
 
490
        void run() {
 
491
            Scope * s = globalScriptEngine->newScope();
 
492
 
 
493
            BSONObjBuilder b;
 
494
            b.appendTimestamp( "a" , 123456789 );
 
495
            b.appendMinKey( "b" );
 
496
            b.appendMaxKey( "c" );
 
497
            b.appendTimestamp( "d" , 1234000 , 9876 );
 
498
 
 
499
 
 
500
            {
 
501
                BSONObj t = b.done();
 
502
                ASSERT_EQUALS( 1234000U , t["d"].timestampTime() );
 
503
                ASSERT_EQUALS( 9876U , t["d"].timestampInc() );
 
504
            }
 
505
 
 
506
            s->setObject( "z" , b.obj() );
 
507
 
 
508
            ASSERT( s->invoke( "y = { a : z.a , b : z.b , c : z.c , d: z.d }" , 0, 0 ) == 0 );
 
509
 
 
510
            BSONObj out = s->getObject( "y" );
 
511
            ASSERT_EQUALS( Timestamp , out["a"].type() );
 
512
            ASSERT_EQUALS( MinKey , out["b"].type() );
 
513
            ASSERT_EQUALS( MaxKey , out["c"].type() );
 
514
            ASSERT_EQUALS( Timestamp , out["d"].type() );
 
515
 
 
516
            ASSERT_EQUALS( 9876U , out["d"].timestampInc() );
 
517
            ASSERT_EQUALS( 1234000U , out["d"].timestampTime() );
 
518
            ASSERT_EQUALS( 123456789U , out["a"].date() );
 
519
 
 
520
            delete s;
 
521
        }
 
522
    };
 
523
 
 
524
    class TypeConservation {
 
525
    public:
 
526
        void run() {
 
527
            Scope * s = globalScriptEngine->newScope();
 
528
 
 
529
            //  --  A  --
 
530
 
 
531
            BSONObj o;
 
532
            {
 
533
                BSONObjBuilder b ;
 
534
                b.append( "a" , (int)5 );
 
535
                b.append( "b" , 5.6 );
 
536
                o = b.obj();
 
537
            }
 
538
            ASSERT_EQUALS( NumberInt , o["a"].type() );
 
539
            ASSERT_EQUALS( NumberDouble , o["b"].type() );
 
540
 
 
541
            s->setObject( "z" , o );
 
542
            s->invoke( "return z" , 0, 0 );
 
543
            BSONObj out = s->getObject( "__returnValue" );
 
544
            ASSERT_EQUALS( 5 , out["a"].number() );
 
545
            ASSERT_EQUALS( 5.6 , out["b"].number() );
 
546
 
 
547
            ASSERT_EQUALS( NumberDouble , out["b"].type() );
 
548
            ASSERT_EQUALS( NumberInt , out["a"].type() );
 
549
 
 
550
            //  --  B  --
 
551
 
 
552
            {
 
553
                BSONObjBuilder b ;
 
554
                b.append( "a" , (int)5 );
 
555
                b.append( "b" , 5.6 );
 
556
                o = b.obj();
 
557
            }
 
558
 
 
559
            s->setObject( "z" , o , false );
 
560
            s->invoke( "return z" , 0, 0 );
 
561
            out = s->getObject( "__returnValue" );
 
562
            ASSERT_EQUALS( 5 , out["a"].number() );
 
563
            ASSERT_EQUALS( 5.6 , out["b"].number() );
 
564
 
 
565
            ASSERT_EQUALS( NumberDouble , out["b"].type() );
 
566
            ASSERT_EQUALS( NumberInt , out["a"].type() );
 
567
 
 
568
 
 
569
            //  -- C --
 
570
 
 
571
            {
 
572
                BSONObjBuilder b ;
 
573
 
 
574
                {
 
575
                    BSONObjBuilder c;
 
576
                    c.append( "0" , 5.5 );
 
577
                    c.append( "1" , 6 );
 
578
                    b.appendArray( "a" , c.obj() );
 
579
                }
 
580
 
 
581
                o = b.obj();
 
582
            }
 
583
 
 
584
            ASSERT_EQUALS( NumberDouble , o["a"].embeddedObjectUserCheck()["0"].type() );
 
585
            ASSERT_EQUALS( NumberInt , o["a"].embeddedObjectUserCheck()["1"].type() );
 
586
 
 
587
            s->setObject( "z" , o , false );
 
588
            out = s->getObject( "z" );
 
589
 
 
590
            ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["0"].type() );
 
591
            ASSERT_EQUALS( NumberInt , out["a"].embeddedObjectUserCheck()["1"].type() );
 
592
 
 
593
            s->invokeSafe( "z.z = 5;" , 0, 0 );
 
594
            out = s->getObject( "z" );
 
595
            ASSERT_EQUALS( 5 , out["z"].number() );
 
596
            ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["0"].type() );
 
597
            // Commenting so that v8 tests will work
 
598
//            ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["1"].type() ); // TODO: this is technically bad, but here to make sure that i understand the behavior
 
599
 
 
600
 
 
601
            // Eliot says I don't have to worry about this case
 
602
 
 
603
//            // -- D --
 
604
//
 
605
//            o = fromjson( "{a:3.0,b:4.5}" );
 
606
//            ASSERT_EQUALS( NumberDouble , o["a"].type() );
 
607
//            ASSERT_EQUALS( NumberDouble , o["b"].type() );
 
608
//
 
609
//            s->setObject( "z" , o , false );
 
610
//            s->invoke( "return z" , BSONObj() );
 
611
//            out = s->getObject( "__returnValue" );
 
612
//            ASSERT_EQUALS( 3 , out["a"].number() );
 
613
//            ASSERT_EQUALS( 4.5 , out["b"].number() );
 
614
//
 
615
//            ASSERT_EQUALS( NumberDouble , out["b"].type() );
 
616
//            ASSERT_EQUALS( NumberDouble , out["a"].type() );
 
617
//
 
618
 
 
619
            delete s;
 
620
        }
 
621
 
 
622
    };
 
623
 
 
624
    class NumberLong {
 
625
    public:
 
626
        void run() {
 
627
            auto_ptr<Scope> s( globalScriptEngine->newScope() );
 
628
            s->localConnect( "blah" );
 
629
            BSONObjBuilder b;
 
630
            long long val = (long long)( 0xbabadeadbeefbaddULL );
 
631
            b.append( "a", val );
 
632
            BSONObj in = b.obj();
 
633
            s->setObject( "a", in );
 
634
            BSONObj out = s->getObject( "a" );
 
635
            ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() );
 
636
 
 
637
            ASSERT( s->exec( "b = {b:a.a}", "foo", false, true, false ) );
 
638
            out = s->getObject( "b" );
 
639
            ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() );
 
640
            if( val != out.firstElement().numberLong() ) {
 
641
                cout << val << endl;
 
642
                cout << out.firstElement().numberLong() << endl;
 
643
                cout << out.toString() << endl;
 
644
                ASSERT_EQUALS( val, out.firstElement().numberLong() );
 
645
            }
 
646
 
 
647
            ASSERT( s->exec( "c = {c:a.a.toString()}", "foo", false, true, false ) );
 
648
            out = s->getObject( "c" );
 
649
            stringstream ss;
 
650
            ss << "NumberLong(\"" << val << "\")";
 
651
            ASSERT_EQUALS( ss.str(), out.firstElement().valuestr() );
 
652
 
 
653
            ASSERT( s->exec( "d = {d:a.a.toNumber()}", "foo", false, true, false ) );
 
654
            out = s->getObject( "d" );
 
655
            ASSERT_EQUALS( NumberDouble, out.firstElement().type() );
 
656
            ASSERT_EQUALS( double( val ), out.firstElement().number() );
 
657
 
 
658
            ASSERT( s->exec( "e = {e:a.a.floatApprox}", "foo", false, true, false ) );
 
659
            out = s->getObject( "e" );
 
660
            ASSERT_EQUALS( NumberDouble, out.firstElement().type() );
 
661
            ASSERT_EQUALS( double( val ), out.firstElement().number() );
 
662
 
 
663
            ASSERT( s->exec( "f = {f:a.a.top}", "foo", false, true, false ) );
 
664
            out = s->getObject( "f" );
 
665
            ASSERT( NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type() );
 
666
 
 
667
            s->setObject( "z", BSON( "z" << (long long)( 4 ) ) );
 
668
            ASSERT( s->exec( "y = {y:z.z.top}", "foo", false, true, false ) );
 
669
            out = s->getObject( "y" );
 
670
            ASSERT_EQUALS( Undefined, out.firstElement().type() );
 
671
 
 
672
            ASSERT( s->exec( "x = {x:z.z.floatApprox}", "foo", false, true, false ) );
 
673
            out = s->getObject( "x" );
 
674
            ASSERT( NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type() );
 
675
            ASSERT_EQUALS( double( 4 ), out.firstElement().number() );
 
676
 
 
677
            ASSERT( s->exec( "w = {w:z.z}", "foo", false, true, false ) );
 
678
            out = s->getObject( "w" );
 
679
            ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() );
 
680
            ASSERT_EQUALS( 4, out.firstElement().numberLong() );
 
681
 
 
682
        }
 
683
    };
 
684
 
 
685
    class NumberLong2 {
 
686
    public:
 
687
        void run() {
 
688
            auto_ptr<Scope> s( globalScriptEngine->newScope() );
 
689
            s->localConnect( "blah" );
 
690
 
 
691
            BSONObj in;
 
692
            {
 
693
                BSONObjBuilder b;
 
694
                b.append( "a" , 5 );
 
695
                b.append( "b" , (long long)5 );
 
696
                b.append( "c" , (long long)pow( 2.0, 29 ) );
 
697
                b.append( "d" , (long long)pow( 2.0, 30 ) );
 
698
                b.append( "e" , (long long)pow( 2.0, 31 ) );
 
699
                b.append( "f" , (long long)pow( 2.0, 45 ) );
 
700
                in = b.obj();
 
701
            }
 
702
            s->setObject( "a" , in );
 
703
 
 
704
            ASSERT( s->exec( "x = tojson( a ); " ,"foo" , false , true , false ) );
 
705
            string outString = s->getString( "x" );
 
706
 
 
707
            ASSERT( s->exec( (string)"y = " + outString , "foo2" , false , true , false ) );
 
708
            BSONObj out = s->getObject( "y" );
 
709
            ASSERT_EQUALS( in , out );
 
710
        }
 
711
    };
 
712
 
 
713
    class NumberLongUnderLimit {
 
714
    public:
 
715
        void run() {
 
716
            auto_ptr<Scope> s( globalScriptEngine->newScope() );
 
717
            s->localConnect( "blah" );
 
718
            BSONObjBuilder b;
 
719
            // limit is 2^53
 
720
            long long val = (long long)( 9007199254740991ULL );
 
721
            b.append( "a", val );
 
722
            BSONObj in = b.obj();
 
723
            s->setObject( "a", in );
 
724
            BSONObj out = s->getObject( "a" );
 
725
            ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() );
 
726
 
 
727
            ASSERT( s->exec( "b = {b:a.a}", "foo", false, true, false ) );
 
728
            out = s->getObject( "b" );
 
729
            ASSERT_EQUALS( mongo::NumberLong, out.firstElement().type() );
 
730
            if( val != out.firstElement().numberLong() ) {
 
731
                cout << val << endl;
 
732
                cout << out.firstElement().numberLong() << endl;
 
733
                cout << out.toString() << endl;
 
734
                ASSERT_EQUALS( val, out.firstElement().numberLong() );
 
735
            }
 
736
 
 
737
            ASSERT( s->exec( "c = {c:a.a.toString()}", "foo", false, true, false ) );
 
738
            out = s->getObject( "c" );
 
739
            stringstream ss;
 
740
            ss << "NumberLong(\"" << val << "\")";
 
741
            ASSERT_EQUALS( ss.str(), out.firstElement().valuestr() );
 
742
 
 
743
            ASSERT( s->exec( "d = {d:a.a.toNumber()}", "foo", false, true, false ) );
 
744
            out = s->getObject( "d" );
 
745
            ASSERT_EQUALS( NumberDouble, out.firstElement().type() );
 
746
            ASSERT_EQUALS( double( val ), out.firstElement().number() );
 
747
 
 
748
            ASSERT( s->exec( "e = {e:a.a.floatApprox}", "foo", false, true, false ) );
 
749
            out = s->getObject( "e" );
 
750
            ASSERT_EQUALS( NumberDouble, out.firstElement().type() );
 
751
            ASSERT_EQUALS( double( val ), out.firstElement().number() );
 
752
 
 
753
            ASSERT( s->exec( "f = {f:a.a.top}", "foo", false, true, false ) );
 
754
            out = s->getObject( "f" );
 
755
            ASSERT( Undefined == out.firstElement().type() );
 
756
        }
 
757
    };
 
758
 
 
759
    class WeirdObjects {
 
760
    public:
 
761
 
 
762
        BSONObj build( int depth ) {
 
763
            BSONObjBuilder b;
 
764
            b.append( "0" , depth );
 
765
            if ( depth > 0 )
 
766
                b.appendArray( "1" , build( depth - 1 ) );
 
767
            return b.obj();
 
768
        }
 
769
 
 
770
        void run() {
 
771
            Scope * s = globalScriptEngine->newScope();
 
772
 
 
773
            s->localConnect( "blah" );
 
774
 
 
775
            for ( int i=5; i<100 ; i += 10 ) {
 
776
                s->setObject( "a" , build(i) , false );
 
777
                s->invokeSafe( "tojson( a )" , 0, 0 );
 
778
 
 
779
                s->setObject( "a" , build(5) , true );
 
780
                s->invokeSafe( "tojson( a )" , 0, 0 );
 
781
            }
 
782
 
 
783
            delete s;
 
784
        }
 
785
    };
 
786
 
 
787
    /**
 
788
     * Test exec() timeout value terminates execution (SERVER-8053)
 
789
     */
 
790
    class ExecTimeout {
 
791
    public:
 
792
        void run() {
 
793
            scoped_ptr<Scope> scope(globalScriptEngine->newScope());
 
794
            scope->localConnect("ExecTimeoutDB");
 
795
            // assert timeout occured
 
796
            ASSERT(!scope->exec("var a = 1; while (true) { ; }",
 
797
                                "ExecTimeout", false, true, false, 1));
 
798
        }
 
799
    };
 
800
 
 
801
    /**
 
802
     * Test exec() timeout value terminates execution (SERVER-8053)
 
803
     */
 
804
    class ExecNoTimeout {
 
805
    public:
 
806
        void run() {
 
807
            scoped_ptr<Scope> scope(globalScriptEngine->newScope());
 
808
            scope->localConnect("ExecNoTimeoutDB");
 
809
            // assert no timeout occured
 
810
            ASSERT(scope->exec("var a = function() { return 1; }",
 
811
                               "ExecNoTimeout", false, true, false, 5 * 60 * 1000));
 
812
        }
 
813
    };
 
814
 
 
815
    /**
 
816
     * Test invoke() timeout value terminates execution (SERVER-8053)
 
817
     */
 
818
    class InvokeTimeout {
 
819
    public:
 
820
        void run() {
 
821
            scoped_ptr<Scope> scope(globalScriptEngine->newScope());
 
822
            scope->localConnect("InvokeTimeoutDB");
 
823
 
 
824
            // scope timeout after 500ms
 
825
            bool caught = false;
 
826
            try {
 
827
                scope->invokeSafe("function() {         "
 
828
                                  "    while (true) { } "
 
829
                                  "}                    ",
 
830
                                  0, 0, 1);
 
831
            } catch (const DBException&) {
 
832
                caught = true;
 
833
            }
 
834
            ASSERT(caught);
 
835
        }
 
836
    };
 
837
 
 
838
    /**
 
839
     * Test invoke() timeout value does not terminate execution (SERVER-8053)
 
840
     */
 
841
    class InvokeNoTimeout {
 
842
    public:
 
843
        void run() {
 
844
            scoped_ptr<Scope> scope(globalScriptEngine->newScope());
 
845
            scope->localConnect("InvokeTimeoutDB");
 
846
 
 
847
            // invoke completes before timeout
 
848
            scope->invokeSafe("function() { "
 
849
                              "  for (var i=0; i<1; i++) { ; } "
 
850
                              "} ",
 
851
                              0, 0, 5 * 60 * 1000);
 
852
        }
 
853
    };
 
854
 
 
855
 
 
856
    void dummy_function_to_force_dbeval_cpp_linking() {
 
857
        BSONObj cmd;
 
858
        BSONObjBuilder result;
 
859
        string errmsg;
 
860
        dbEval( "test", cmd, result, errmsg);
 
861
        verify(0);
 
862
    }
 
863
 
 
864
    DBDirectClient client;
 
865
 
 
866
    class Utf8Check {
 
867
    public:
 
868
        Utf8Check() { reset(); }
 
869
        ~Utf8Check() { reset(); }
 
870
        void run() {
 
871
            if( !globalScriptEngine->utf8Ok() ) {
 
872
                log() << "warning: utf8 not supported" << endl;
 
873
                return;
 
874
            }
 
875
            string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}";
 
876
            BSONObj utf8Obj = fromjson( utf8ObjSpec );
 
877
            client.insert( ns(), utf8Obj );
 
878
            client.eval( "unittest", "v = db.jstests.utf8check.findOne(); db.jstests.utf8check.remove( {} ); db.jstests.utf8check.insert( v );" );
 
879
            check( utf8Obj, client.findOne( ns(), BSONObj() ) );
 
880
        }
 
881
    private:
 
882
        void check( const BSONObj &one, const BSONObj &two ) {
 
883
            if ( one.woCompare( two ) != 0 ) {
 
884
                static string fail = string( "Assertion failure expected " ) + one.toString() + ", got " + two.toString();
 
885
                FAIL( fail.c_str() );
 
886
            }
 
887
        }
 
888
        void reset() {
 
889
            client.dropCollection( ns() );
 
890
        }
 
891
        static const char *ns() { return "unittest.jstests.utf8check"; }
 
892
    };
 
893
 
 
894
    class LongUtf8String {
 
895
    public:
 
896
        LongUtf8String() { reset(); }
 
897
        ~LongUtf8String() { reset(); }
 
898
        void run() {
 
899
            if( !globalScriptEngine->utf8Ok() )
 
900
                return;
 
901
            client.eval( "unittest", "db.jstests.longutf8string.save( {_id:'\\uffff\\uffff\\uffff\\uffff'} )" );
 
902
        }
 
903
    private:
 
904
        void reset() {
 
905
            client.dropCollection( ns() );
 
906
        }
 
907
        static const char *ns() { return "unittest.jstests.longutf8string"; }
 
908
    };
 
909
 
 
910
    class InvalidUTF8Check {
 
911
    public:
 
912
        void run() {
 
913
            if( !globalScriptEngine->utf8Ok() )
 
914
                return;
 
915
 
 
916
            auto_ptr<Scope> s;
 
917
            s.reset( globalScriptEngine->newScope() );
 
918
 
 
919
            BSONObj b;
 
920
            {
 
921
                char crap[5];
 
922
 
 
923
                crap[0] = (char) 128;
 
924
                crap[1] = 17;
 
925
                crap[2] = (char) 128;
 
926
                crap[3] = 17;
 
927
                crap[4] = 0;
 
928
 
 
929
                BSONObjBuilder bb;
 
930
                bb.append( "x" , crap );
 
931
                b = bb.obj();
 
932
            }
 
933
 
 
934
            //cout << "ELIOT: " << b.jsonString() << endl;
 
935
            // its ok  if this is handled by js, just can't create a c++ exception
 
936
            s->invoke( "x=this.x.length;" , 0, &b );
 
937
        }
 
938
    };
 
939
 
 
940
    class CodeTests {
 
941
    public:
 
942
        void run() {
 
943
            Scope * s = globalScriptEngine->newScope();
 
944
 
 
945
            {
 
946
                BSONObjBuilder b;
 
947
                b.append( "a" , 1 );
 
948
                b.appendCode( "b" , "function(){ out.b = 11; }" );
 
949
                b.appendCodeWScope( "c" , "function(){ out.c = 12; }" , BSONObj() );
 
950
                b.appendCodeWScope( "d" , "function(){ out.d = 13 + bleh; }" , BSON( "bleh" << 5 ) );
 
951
                s->setObject( "foo" , b.obj() );
 
952
            }
 
953
 
 
954
            s->invokeSafe( "out = {}; out.a = foo.a; foo.b(); foo.c();" , 0, 0 );
 
955
            BSONObj out = s->getObject( "out" );
 
956
 
 
957
            ASSERT_EQUALS( 1 , out["a"].number() );
 
958
            ASSERT_EQUALS( 11 , out["b"].number() );
 
959
            ASSERT_EQUALS( 12 , out["c"].number() );
 
960
 
 
961
            // Guess we don't care about this
 
962
            //s->invokeSafe( "foo.d() " , BSONObj() );
 
963
            //out = s->getObject( "out" );
 
964
            //ASSERT_EQUALS( 18 , out["d"].number() );
 
965
 
 
966
 
 
967
            delete s;
 
968
        }
 
969
    };
 
970
 
 
971
    namespace RoundTripTests {
 
972
 
 
973
        // Inherit from this class to test round tripping of JSON objects
 
974
        class TestRoundTrip {
 
975
        public:
 
976
            virtual ~TestRoundTrip() {}
 
977
            void run() {
 
978
 
 
979
                // Insert in Javascript -> Find using DBDirectClient
 
980
 
 
981
                // Drop the collection
 
982
                client.dropCollection( "unittest.testroundtrip" );
 
983
 
 
984
                // Insert in Javascript
 
985
                stringstream jsInsert;
 
986
                jsInsert << "db.testroundtrip.insert(" << jsonIn() << ")";
 
987
                ASSERT_TRUE( client.eval( "unittest" , jsInsert.str() ) );
 
988
 
 
989
                // Find using DBDirectClient
 
990
                BSONObj excludeIdProjection = BSON( "_id" << 0 );
 
991
                BSONObj directFind = client.findOne( "unittest.testroundtrip",
 
992
                                                                "",
 
993
                                                                &excludeIdProjection);
 
994
                bsonEquals( bson(), directFind );
 
995
 
 
996
 
 
997
                // Insert using DBDirectClient -> Find in Javascript
 
998
 
 
999
                // Drop the collection
 
1000
                client.dropCollection( "unittest.testroundtrip" );
 
1001
 
 
1002
                // Insert using DBDirectClient
 
1003
                client.insert( "unittest.testroundtrip" , bson() );
 
1004
 
 
1005
                // Find in Javascript
 
1006
                stringstream jsFind;
 
1007
                jsFind << "dbref = db.testroundtrip.findOne( { } , { _id : 0 } )\n"
 
1008
                                << "assert.eq(dbref, " << jsonOut() << ")";
 
1009
                ASSERT_TRUE( client.eval( "unittest" , jsFind.str() ) );
 
1010
            }
 
1011
        protected:
 
1012
 
 
1013
            // Methods that must be defined by child classes
 
1014
            virtual BSONObj bson() const = 0;
 
1015
            virtual string json() const = 0;
 
1016
 
 
1017
            // This can be overriden if a different meaning of equality besides woCompare is needed
 
1018
            virtual void bsonEquals( const BSONObj &expected, const BSONObj &actual ) {
 
1019
                if ( expected.woCompare( actual ) ) {
 
1020
                    out() << "want:" << expected.jsonString() << " size: " << expected.objsize() << endl;
 
1021
                    out() << "got :" << actual.jsonString() << " size: " << actual.objsize() << endl;
 
1022
                    out() << expected.hexDump() << endl;
 
1023
                    out() << actual.hexDump() << endl;
 
1024
                }
 
1025
                ASSERT( !expected.woCompare( actual ) );
 
1026
            }
 
1027
 
 
1028
            // This can be overriden if the JSON representation is altered on the round trip
 
1029
            virtual string jsonIn() const {
 
1030
                return json();
 
1031
            }
 
1032
            virtual string jsonOut() const {
 
1033
                return json();
 
1034
            }
 
1035
        };
 
1036
 
 
1037
        class DBRefTest : public TestRoundTrip {
 
1038
            virtual BSONObj bson() const {
 
1039
                BSONObjBuilder b;
 
1040
                OID o;
 
1041
                memset( &o, 0, 12 );
 
1042
                BSONObjBuilder subBuilder(b.subobjStart("a"));
 
1043
                subBuilder.append("$ref", "ns");
 
1044
                subBuilder.append("$id", o);
 
1045
                subBuilder.done();
 
1046
                return b.obj();
 
1047
            }
 
1048
            virtual string json() const {
 
1049
                return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
 
1050
            }
 
1051
 
 
1052
            // A "fetch" function is added to the DBRef object when it is inserted using the
 
1053
            // constructor, so we need to compare the fields individually
 
1054
            virtual void bsonEquals( const BSONObj &expected, const BSONObj &actual ) {
 
1055
                ASSERT_EQUALS( expected["a"].type() , actual["a"].type() );
 
1056
                ASSERT_EQUALS( expected["a"]["$id"].OID() , actual["a"]["$id"].OID() );
 
1057
                ASSERT_EQUALS( expected["a"]["$ref"].String() , actual["a"]["$ref"].String() );
 
1058
            }
 
1059
        };
 
1060
 
 
1061
        class DBPointerTest : public TestRoundTrip {
 
1062
            virtual BSONObj bson() const {
 
1063
                BSONObjBuilder b;
 
1064
                OID o;
 
1065
                memset( &o, 0, 12 );
 
1066
                b.appendDBRef( "a" , "ns" , o );
 
1067
                return b.obj();
 
1068
            }
 
1069
            virtual string json() const {
 
1070
                return "{ \"a\" : DBPointer( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
 
1071
            }
 
1072
        };
 
1073
 
 
1074
        class InformalDBRefTest : public TestRoundTrip {
 
1075
            virtual BSONObj bson() const {
 
1076
                BSONObjBuilder b;
 
1077
                BSONObjBuilder subBuilder(b.subobjStart("a"));
 
1078
                subBuilder.append("$ref", "ns");
 
1079
                subBuilder.append("$id", "000000000000000000000000");
 
1080
                subBuilder.done();
 
1081
                return b.obj();
 
1082
            }
 
1083
 
 
1084
            // Don't need to return anything because we are overriding both jsonOut and jsonIn
 
1085
            virtual string json() const { return ""; }
 
1086
 
 
1087
            // Need to override these because the JSON doesn't actually round trip.
 
1088
            // An object with "$ref" and "$id" fields is handled specially and different on the way out.
 
1089
            virtual string jsonOut() const {
 
1090
                return "{ \"a\" : DBRef( \"ns\", \"000000000000000000000000\" ) }";
 
1091
            }
 
1092
            virtual string jsonIn() const {
 
1093
                stringstream ss;
 
1094
                ss << "{ \"a\" : { \"$ref\" : \"ns\" , " <<
 
1095
                                "\"$id\" : \"000000000000000000000000\" } }";
 
1096
                return ss.str();
 
1097
            }
 
1098
        };
 
1099
 
 
1100
        class InformalDBRefOIDTest : public TestRoundTrip {
 
1101
            virtual BSONObj bson() const {
 
1102
                BSONObjBuilder b;
 
1103
                OID o;
 
1104
                memset( &o, 0, 12 );
 
1105
                BSONObjBuilder subBuilder(b.subobjStart("a"));
 
1106
                subBuilder.append("$ref", "ns");
 
1107
                subBuilder.append("$id", o);
 
1108
                subBuilder.done();
 
1109
                return b.obj();
 
1110
            }
 
1111
 
 
1112
            // Don't need to return anything because we are overriding both jsonOut and jsonIn
 
1113
            virtual string json() const { return ""; }
 
1114
 
 
1115
            // Need to override these because the JSON doesn't actually round trip.
 
1116
            // An object with "$ref" and "$id" fields is handled specially and different on the way out.
 
1117
            virtual string jsonOut() const {
 
1118
                return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
 
1119
            }
 
1120
            virtual string jsonIn() const {
 
1121
                stringstream ss;
 
1122
                ss << "{ \"a\" : { \"$ref\" : \"ns\" , " <<
 
1123
                                "\"$id\" : ObjectId( \"000000000000000000000000\" ) } }";
 
1124
                return ss.str();
 
1125
            }
 
1126
        };
 
1127
 
 
1128
        class InformalDBRefExtraFieldTest : public TestRoundTrip {
 
1129
            virtual BSONObj bson() const {
 
1130
                BSONObjBuilder b;
 
1131
                OID o;
 
1132
                memset( &o, 0, 12 );
 
1133
                BSONObjBuilder subBuilder(b.subobjStart("a"));
 
1134
                subBuilder.append("$ref", "ns");
 
1135
                subBuilder.append("$id", o);
 
1136
                subBuilder.append("otherfield", "value");
 
1137
                subBuilder.done();
 
1138
                return b.obj();
 
1139
            }
 
1140
 
 
1141
            // Don't need to return anything because we are overriding both jsonOut and jsonIn
 
1142
            virtual string json() const { return ""; }
 
1143
 
 
1144
            // Need to override these because the JSON doesn't actually round trip.
 
1145
            // An object with "$ref" and "$id" fields is handled specially and different on the way out.
 
1146
            virtual string jsonOut() const {
 
1147
                return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
 
1148
            }
 
1149
            virtual string jsonIn() const {
 
1150
                stringstream ss;
 
1151
                ss << "{ \"a\" : { \"$ref\" : \"ns\" , " <<
 
1152
                                "\"$id\" : ObjectId( \"000000000000000000000000\" ) , " <<
 
1153
                                "\"otherfield\" : \"value\" } }";
 
1154
                return ss.str();
 
1155
            }
 
1156
        };
 
1157
 
 
1158
    } // namespace RoundTripTests
 
1159
 
 
1160
    class BinDataType {
 
1161
    public:
 
1162
 
 
1163
        void pp( const char * s , BSONElement e ) {
 
1164
            int len;
 
1165
            const char * data = e.binData( len );
 
1166
            cout << s << ":" << e.binDataType() << "\t" << len << endl;
 
1167
            cout << "\t";
 
1168
            for ( int i=0; i<len; i++ )
 
1169
                cout << (int)(data[i]) << " ";
 
1170
            cout << endl;
 
1171
        }
 
1172
 
 
1173
        void run() {
 
1174
            Scope * s = globalScriptEngine->newScope();
 
1175
            s->localConnect( "asd" );
 
1176
            const char * foo = "asdas\0asdasd";
 
1177
            const char * base64 = "YXNkYXMAYXNkYXNk";
 
1178
 
 
1179
            BSONObj in;
 
1180
            {
 
1181
                BSONObjBuilder b;
 
1182
                b.append( "a" , 7 );
 
1183
                b.appendBinData( "b" , 12 , BinDataGeneral , foo );
 
1184
                in = b.obj();
 
1185
                s->setObject( "x" , in );
 
1186
            }
 
1187
 
 
1188
            s->invokeSafe( "myb = x.b; print( myb ); printjson( myb );" , 0, 0 );
 
1189
            s->invokeSafe( "y = { c : myb };" , 0, 0 );
 
1190
 
 
1191
            BSONObj out = s->getObject( "y" );
 
1192
            ASSERT_EQUALS( BinData , out["c"].type() );
 
1193
//            pp( "in " , in["b"] );
 
1194
//            pp( "out" , out["c"] );
 
1195
            ASSERT_EQUALS( 0 , in["b"].woCompare( out["c"] , false ) );
 
1196
 
 
1197
            // check that BinData js class is utilized
 
1198
            s->invokeSafe( "q = x.b.toString();", 0, 0 );
 
1199
            stringstream expected;
 
1200
            expected << "BinData(" << BinDataGeneral << ",\"" << base64 << "\")";
 
1201
            ASSERT_EQUALS( expected.str(), s->getString( "q" ) );
 
1202
 
 
1203
            stringstream scriptBuilder;
 
1204
            scriptBuilder << "z = { c : new BinData( " << BinDataGeneral << ", \"" << base64 << "\" ) };";
 
1205
            string script = scriptBuilder.str();
 
1206
            s->invokeSafe( script.c_str(), 0, 0 );
 
1207
            out = s->getObject( "z" );
 
1208
//            pp( "out" , out["c"] );
 
1209
            ASSERT_EQUALS( 0 , in["b"].woCompare( out["c"] , false ) );
 
1210
 
 
1211
            s->invokeSafe( "a = { f: new BinData( 128, \"\" ) };", 0, 0 );
 
1212
            out = s->getObject( "a" );
 
1213
            int len = -1;
 
1214
            out[ "f" ].binData( len );
 
1215
            ASSERT_EQUALS( 0, len );
 
1216
            ASSERT_EQUALS( 128, out[ "f" ].binDataType() );
 
1217
 
 
1218
            delete s;
 
1219
        }
 
1220
    };
 
1221
 
 
1222
    class VarTests {
 
1223
    public:
 
1224
        void run() {
 
1225
            Scope * s = globalScriptEngine->newScope();
 
1226
 
 
1227
            ASSERT( s->exec( "a = 5;" , "a" , false , true , false ) );
 
1228
            ASSERT_EQUALS( 5 , s->getNumber("a" ) );
 
1229
 
 
1230
            ASSERT( s->exec( "var b = 6;" , "b" , false , true , false ) );
 
1231
            ASSERT_EQUALS( 6 , s->getNumber("b" ) );
 
1232
            delete s;
 
1233
        }
 
1234
    };
 
1235
 
 
1236
    class Speed1 {
 
1237
    public:
 
1238
        void run() {
 
1239
            BSONObj start = BSON( "x" << 5.0 );
 
1240
            BSONObj empty;
 
1241
 
 
1242
            auto_ptr<Scope> s;
 
1243
            s.reset( globalScriptEngine->newScope() );
 
1244
 
 
1245
            ScriptingFunction f = s->createFunction( "return this.x + 6;" );
 
1246
 
 
1247
            Timer t;
 
1248
            double n = 0;
 
1249
            for ( ; n < 10000 ; n++ ) {
 
1250
                s->invoke( f , &empty, &start );
 
1251
                ASSERT_EQUALS( 11 , s->getNumber( "__returnValue" ) );
 
1252
            }
 
1253
            //cout << "speed1: " << ( n / t.millis() ) << " ops/ms" << endl;
 
1254
        }
 
1255
    };
 
1256
 
 
1257
    class ScopeOut {
 
1258
    public:
 
1259
        void run() {
 
1260
            auto_ptr<Scope> s;
 
1261
            s.reset( globalScriptEngine->newScope() );
 
1262
 
 
1263
            s->invokeSafe( "x = 5;" , 0, 0 );
 
1264
            {
 
1265
                BSONObjBuilder b;
 
1266
                s->append( b , "z" , "x" );
 
1267
                ASSERT_EQUALS( BSON( "z" << 5 ) , b.obj() );
 
1268
            }
 
1269
 
 
1270
            s->invokeSafe( "x = function(){ return 17; }" , 0, 0 );
 
1271
            BSONObj temp;
 
1272
            {
 
1273
                BSONObjBuilder b;
 
1274
                s->append( b , "z" , "x" );
 
1275
                temp = b.obj();
 
1276
            }
 
1277
 
 
1278
            s->invokeSafe( "foo = this.z();" , 0, &temp );
 
1279
            ASSERT_EQUALS( 17 , s->getNumber( "foo" ) );
 
1280
        }
 
1281
    };
 
1282
 
 
1283
    class RenameTest {
 
1284
    public:
 
1285
        void run() {
 
1286
            auto_ptr<Scope> s;
 
1287
            s.reset( globalScriptEngine->newScope() );
 
1288
 
 
1289
            s->setNumber( "x" , 5 );
 
1290
            ASSERT_EQUALS( 5 , s->getNumber( "x" ) );
 
1291
            ASSERT_EQUALS( Undefined , s->type( "y" ) );
 
1292
 
 
1293
            s->rename( "x" , "y" );
 
1294
            ASSERT_EQUALS( 5 , s->getNumber( "y" ) );
 
1295
            ASSERT_EQUALS( Undefined , s->type( "x" ) );
 
1296
 
 
1297
            s->rename( "y" , "x" );
 
1298
            ASSERT_EQUALS( 5 , s->getNumber( "x" ) );
 
1299
            ASSERT_EQUALS( Undefined , s->type( "y" ) );
 
1300
        }
 
1301
    };
 
1302
 
 
1303
 
 
1304
    class InvalidStoredJS {
 
1305
    public:
 
1306
        void run() {
 
1307
            BSONObjBuilder query;
 
1308
            query.append( "_id" , "invalidstoredjs1" );
 
1309
            
 
1310
            BSONObjBuilder update;
 
1311
            update.append( "_id" , "invalidstoredjs1" );
 
1312
            update.appendCode( "value" , "function () { db.test.find().forEach(function(obj) { continue; }); }" );
 
1313
            client.update( "test.system.js" , query.obj() , update.obj() , true /* upsert */ );
 
1314
 
 
1315
            scoped_ptr<Scope> s( globalScriptEngine->newScope() );
 
1316
            client.eval( "test" , "invalidstoredjs1()" );
 
1317
            
 
1318
            BSONObj info;
 
1319
            BSONElement ret;
 
1320
            ASSERT( client.eval( "test" , "return 5 + 12" , info , ret ) );
 
1321
            ASSERT_EQUALS( 17 , ret.number() );
 
1322
        }
 
1323
    };
 
1324
 
 
1325
    class All : public Suite {
 
1326
    public:
 
1327
        All() : Suite( "js" ) {
 
1328
            // Initialize the Javascript interpreter
 
1329
            ScriptEngine::setup();
 
1330
        }
 
1331
 
 
1332
        void setupTests() {
 
1333
            add< BuiltinTests >();
 
1334
            add< BasicScope >();
 
1335
            add< ResetScope >();
 
1336
            add< FalseTests >();
 
1337
            add< SimpleFunctions >();
 
1338
            add< ExecLogError >();
 
1339
            add< InvokeLogError >();
 
1340
            add< ExecTimeout >();
 
1341
            add< ExecNoTimeout >();
 
1342
            add< InvokeTimeout >();
 
1343
            add< InvokeNoTimeout >();
 
1344
 
 
1345
            add< ObjectMapping >();
 
1346
            add< ObjectDecoding >();
 
1347
            add< JSOIDTests >();
 
1348
            add< SetImplicit >();
 
1349
            add< ObjectModReadonlyTests >();
 
1350
            add< OtherJSTypes >();
 
1351
            add< SpecialDBTypes >();
 
1352
            add< TypeConservation >();
 
1353
            add< NumberLong >();
 
1354
            add< NumberLong2 >();
 
1355
            add< RenameTest >();
 
1356
 
 
1357
            add< WeirdObjects >();
 
1358
            add< CodeTests >();
 
1359
            add< BinDataType >();
 
1360
 
 
1361
            add< VarTests >();
 
1362
 
 
1363
            add< Speed1 >();
 
1364
 
 
1365
            add< InvalidUTF8Check >();
 
1366
            add< Utf8Check >();
 
1367
            add< LongUtf8String >();
 
1368
 
 
1369
            add< ScopeOut >();
 
1370
            add< InvalidStoredJS >();
 
1371
 
 
1372
            add< RoundTripTests::DBRefTest >();
 
1373
            add< RoundTripTests::DBPointerTest >();
 
1374
            add< RoundTripTests::InformalDBRefTest >();
 
1375
            add< RoundTripTests::InformalDBRefOIDTest >();
 
1376
            add< RoundTripTests::InformalDBRefExtraFieldTest >();
 
1377
        }
 
1378
    } myall;
 
1379
 
 
1380
} // namespace JavaJSTests
 
1381