Commits

vadimtsushko committed 9f4d884

Update operation added.
Example simpleUpdate.dart

Comments (0)

Files changed (29)

 tests/bson/BsonObjectIdTest.dart
-lib/networking/mongo_update_message.dart
-tests/AdHoq.dart
+tests/allTests.dart
 tests/CursorTest.dart
 tests/DbCommandTest.dart
+examples/simpleUpdate.dart
 tests/bson/BsonImplTest.dart
 tests/MCollectionTest.dart
 lib/bson/bson.dart
 tests/bson/BSonTypesTest.dart
+third_party/testing/unittest/unittest_html.dart
+third_party/testing/unittest/unittest_node.dart
 tests/ConnectionTest.dart
+third_party/testing/unittest/unittest_dartest.dart
+third_party/testing/unittest/unittest_dom.dart
+third_party/testing/unittest/unittest_vm.dart
 lib/mongo.dart
 tests/bson/BsonBinaryTest.dart
 examples/simple.dart

examples/simple.dart

     db.close();
   });
     
-}
+}

examples/simpleUpdate.dart

+#import("../lib/mongo.dart");
+#import("dart:builtin");
+main(){
+  Db db = new Db('mongo-dart-test');
+  db.open();  
+  MCollection coll = db.collection('collection-for-save');
+  coll.remove();  
+  List toInsert = [
+                   {"name":"a", "value": 10},
+                   {"name":"b", "value": 20},
+                   {"name":"c", "value": 30},
+                   {"name":"d", "value": 40}
+                 ];
+  coll.insertAll(toInsert);
+  coll.findOne({"name":"c"}).chain((v1){
+    print("Record c: $v1");
+    v1["value"] = 31;    
+    coll.save(v1);
+    return coll.findOne({"name":"c"});
+  }).then((v2){
+    print("Record c after update: $v2");
+    db.close();
+  });    
+}

lib/bson/bson.dart

 #source("bson_impl.dart");
 #source("bson_double.dart");
 #source("bson_null.dart");
+#source("bson_boolean.dart");
 
 

lib/bson/bson_boolean.dart

+class BsonBoolean extends BsonObject{
+  bool data;
+  BsonBoolean(this.data);
+  get value()=>data;
+  byteLength()=>1;
+  int get typeByte() => BSON.BSON_DATA_BOOLEAN;
+  packValue(Binary buffer){
+     buffer.writeByte(data?1:0);
+  }
+  unpackValue(Binary buffer){
+     var b = buffer.readByte();
+     if (b == 1){
+       data = true;
+     }
+     else{
+       data = false;
+     }
+  }
+}

lib/bson/bson_type.dart

   }        
   if (value === null){
     return new BsonNull();
-  }        
+  }
+  if (value === true || value === false){
+    return new BsonBoolean(value);
+  }
   throw new Exception("Not implemented for $value");           
 }  
 
       return new ObjectId();
     case BSON.BSON_DATA_NULL:
       return new BsonNull();
+    case BSON.BSON_DATA_BOOLEAN:
+      return new BsonBoolean(false);
 
     default:
       throw new Exception("Not implemented for BSON TYPE $typeByte");           

lib/bson/objectid.dart

     id.writeInt(Statics.nextIncrement,3,forceBigEndian:true);
   }  
   String toString()=>"ObjectId(${id.toHexString()})";
+  int get typeByte() => BSON.BSON_DATA_OID;
   get value() => this;
   int byteLength() => 12;
   unpackValue(Binary buffer){
      id.bytes.setRange(0,12,buffer.bytes,buffer.offset);
      buffer.offset += 12;
   }
-
+  packValue(Binary buffer){
+    buffer.bytes.setRange(buffer.offset,12,id.bytes);
+    buffer.offset += 12;
+ }
 }
   }    
   open(){
     connection.connect();
+    return this;
   }
-  Future<bool> executeDbCommand(MongoMessage message){
+  Future<Map> executeDbCommand(MongoMessage message){
       Completer<bool> result = new Completer();
       connection.query(message).then((replyMessage){
         if (replyMessage.documents[0]["ok"] == 1.0){
-          print(replyMessage.documents[0]);
-          result.complete(true);
+          result.complete(replyMessage.documents[0]);
         } else {
           String errMsg = "Error executing Db command";
           if (replyMessage.documents[0].containsKey("errmsg")){
             errMsg = replyMessage.documents[0]["errmsg"];
           }
           print("Error: $errMsg");
-          result.complete(false);
+          result.complete(replyMessage.documents[0]);
         }         
       });
     return result.future;        
     Completer completer = new Completer();
     collectionsInfoCursor(collectionName).toList().then((v){
       if (v.length == 1){
-//        print("drop collection");
         executeDbCommand(DbCommand.createDropCollectionCommand(this,collectionName))
           .then((res)=>completer.complete(res));
         } else{
   
   Future<bool> getLastError(){    
     return executeDbCommand(DbCommand.createGetLastErrorCommand(this));
-  }  
+  }
+  Future<bool> wait(){
+    return getLastError();
+  }
   close(){
     connection.close();
   }

lib/mcollection.dart

   String collectionName;
   MCollection(this.db, this.collectionName){}  
   String fullName() => "${db.databaseName}.$collectionName";
-  saveAll(List<Map> documents){
-    insertAll(documents);
-  } 
   save(Map document){
-    return saveAll([document]);  
+    if (document.containsKey("_id")){
+      update({"_id": document["_id"]}, document);
+    } 
+    else{
+      insert(document);
+    }
   }
   insertAll(List<Map> documents){
     MongoInsertMessage insertMessage = new MongoInsertMessage(fullName(),documents);
     return db.executeDbCommand(insertMessage);   
   }
+  update(Map selector, Map document){
+    MongoUpdateMessage message = new MongoUpdateMessage(fullName(),selector, document, 0);
+    return db.executeMessage(message);   
+  }
+  
   insert(Map document){
     return insertAll([document]);
   } 
 #source("networking/mongo_insert_message.dart");
 #source("networking/mongo_remove_message.dart");
 #source("networking/mongo_getmore_message.dart");
+#source("networking/mongo_update_message.dart");
 #source("networking/server_config.dart");
 #source("networking/dbcommand.dart");
 #source("db.dart");

lib/networking/connection.dart

     socket.onWrite = null;
     socket.onError = null;
     socket.close();
-//    print(replyCompleters);
     replyCompleters.clear();    
   }
   int sendData(Binary msg){
   sendBufferFromTimer() => sendBuffer("from Timer");
   sendBufferFromOnWrite() => sendBuffer("from OnWrite");
   sendBuffer(String origin){
-//    print(origin);
     getNextBufferToSend();
-    if (bufferToSend !== null){
+    if (bufferToSend !== null){      
       bufferToSend.offset += socket.writeList(bufferToSend.bytes,
         bufferToSend.offset,bufferToSend.bytes.length-bufferToSend.offset);
       if (!bufferToSend.atEnd()){
-//        print("${bufferToSend.offset}");
-      }      
-//      new Timer(0,(t)=>sendBufferFromTimer());
-      sendBuffer("Recursevly");
+//        print("Buffer not send fully, offset: ${bufferToSend.offset}");
+      }
+      new Timer(0,(t)=>sendBufferFromTimer());
+      //sendBuffer("Recursevly");              
     }        
     else {
       socket.onWrite = null;        
     sendBuffer("From query");
     return completer.future;
   }
-  execute(MongoMessage message){    
+  execute(MongoMessage message){
+//    print(message.messageLength);
     sendQueue.addLast(message.serialize());    
-    socket.onWrite = sendBufferFromOnWrite;  
+    socket.onWrite = sendBufferFromOnWrite;
+//    socket.onError = ()=>print("Error");
+//    socket.onError = ()=>print("Close");
   }
 }

lib/networking/mongo_message.dart

       buffer.writeInt(requestId);
       buffer.writeInt(0); // responseTo not used in requests sent by client
       buffer.writeInt(opcode);
+      if (messageLength < 0){
+        throw "Error in message length";
+      }
   }
 
 }

lib/networking/mongo_update_message.dart

   int flags;
   int numberToSkip;
   int numberToReturn;
-  BsonMap _query;
-  BsonMap _fields;
+  BsonMap _selector;
+  BsonMap _document;
   MongoUpdateMessage(String collectionFullName,
-            this.flags,
-            this.numberToSkip,
-            this.numberToReturn,
-            Map query,
-            Map fields){
+            Map selector,
+            Map document,
+            this.flags            
+            ){
     _collectionFullName = new BsonCString(collectionFullName);
-    _query = new BsonMap(query);
-    if (fields !== null){
-      _fields = new BsonMap(fields);
-    }
-    opcode = MongoMessage.Query;    
+    _selector = new BsonMap(selector);
+    _document = new BsonMap(document);    
+    opcode = MongoMessage.Update;    
   }
   int get messageLength(){
-    int result = 16+4+_collectionFullName.byteLength()+4+4+_query.byteLength();
-    if (_fields !== null){
-      result += _fields.byteLength();
-    }
-    return result;
+    return 16+4+_collectionFullName.byteLength()+4+_selector.byteLength()+_document.byteLength();
   }
   Binary serialize(){
     Binary buffer = new Binary(messageLength);
     writeMessageHeaderTo(buffer);
+    buffer.writeInt(0);
+    _collectionFullName.packValue(buffer);
     buffer.writeInt(flags);
-    _collectionFullName.packValue(buffer);
-    buffer.writeInt(numberToSkip);
-    buffer.writeInt(numberToReturn);
-    _query.packValue(buffer);
+    _selector.packValue(buffer);
+    _document.packValue(buffer);
     buffer.offset = 0;
     return buffer;
   }

mongo.status

Empty file removed.

tests/AdHoq.dart

-#import("dart:io");
-#import("dart:utf");
-#import("dart:builtin");
-main(){
-  print(decodeUtf8([123,2443,34]));
-}
-

tests/ConnectionTest.dart

+#library("connection_test");
 #import("../lib/mongo.dart");
 #import("../lib/bson/bson.dart");
 #import("dart:io");
 #import('dart:builtin');
+#import('../third_party/testing/unittest/unittest_vm.dart');
 testPing(){
   Connection conn = new Connection();
   conn.connect();
 
 }
 main(){
-  testPing();
-  testStudent();
+  group("Connection tests:", (){
+    test("Test ping",testPing);
+    test("Test testStudent",testStudent);
+  });
 }

tests/CursorTest.dart

+#library("cursor_tests");
 #import("../lib/mongo.dart");
 #import("dart:io");
-#import('dart:builtin'); 
+#import('dart:builtin');
+#import('../third_party/testing/unittest/unittest_vm.dart');
 testCursorCreation(){
   Db db = new Db('db');
   MCollection collection = db.collection('student');
   res.then((v){
 //    print(v);
     Expect.mapEquals({'ok': 1.0},v);
+    db.close();
   });
 }
 testNextObjectToEnd(){
   db.open();
   MCollection collection = db.collection('new_big_collection');
   collection.remove();
-  for (int n=0;n < 1000; n++){
+  for (int n=0;n < 100; n++){
     collection.insert({"a":n});
   }
   Cursor cursor = new Cursor(db,collection,limit:10);  
-  cursor.nextObject().then((v){
-    print(v);
-    Expect.isTrue(cursor.state == Cursor.OPEN);
-    print("CursorId = ${cursor.cursorId}");
+  cursor.nextObject().then((v){  
+    Expect.isTrue(cursor.state == Cursor.OPEN);  
     Expect.isTrue(cursor.cursorId > 0);
     db.close();
     });
   var res;
   Db db = new Db('test');
   db.open();
-  MCollection collection = db.collection('new_big_collection');
+  MCollection collection = db.collection('new_big_collection1');
   collection.remove();
-  for (int n=0;n < 1000; n++){
-    collection.insert({"a":n});
-  }
+  db.getLastError().then((dummy){
+    List toInsert = new List();
+    for (int n=0;n < 1000; n++){
+      toInsert.add({"a":n});
+    }
+    collection.insertAll(toInsert);
+  });
   int count = 0;
-  Cursor cursor = new Cursor(db,collection,limit:10);  
-  cursor.each((v)=>count++).then((v){
+  db.getLastError().then((dummy){
+    Cursor cursor = new Cursor(db,collection,limit:10);  
+      cursor.each((v){
+            count++;
+//            print(v);
+      }).then((v){
     print(count);
     Expect.equals(1000, count);
     Expect.equals(0,cursor.cursorId);
     Expect.equals(Cursor.CLOSED,cursor.state);
     db.close();
     });
+  });  
 }
 
 main(){
-  testCursorCreation();
-  testPingRaw();
-  testCursorWithOpenServerCursor();
-  testCursorGetMore();
-//  testNextObject();
-//  testNextObjectToEnd();
-//  testEach();
-//  testEachWithGetMore();
+  group("Cursor tests:", (){
+    test("testCursorCreation",testCursorCreation);
+    test("testPingRaw",testPingRaw);    
+    test("testNextObject",testNextObject);    
+    test("testCursorWithOpenServerCursor",testCursorWithOpenServerCursor);
+    test("testCursorGetMore",testCursorGetMore);    
+  });
 }

tests/DbCommandTest.dart

+#library("dbcommand");
 #import("../lib/mongo.dart");
+#import('../third_party/testing/unittest/unittest_vm.dart');
 testDbCommandCreation(){
   Db db = new Db('test');
   DbCommand dbCommand = new DbCommand(db,"student",0,0,1,{},{});
   Future<Map> mapFuture = db.executeQueryMessage(pingCommand);
   mapFuture.then((msg) {
     Expect.mapEquals({'ok': 1},msg.documents[0]);
+    db.close();
   });
 }
-testDropCollectionCommand(){
-  Db db = new Db("test");
-  db.open();
-//  print(isFutureThrowsException(db.dropCollection("collection_with_that_name_does_not_exists")));
-//  db.dropCollection("newColl");
-//  db.collection("student").drop().then((v)=>print("Student collection dropped"));
-}
-
-isFutureThrowsException(Future future){
-  bool result = false;  
-  future.handleException((ex){
-    result = true;
-    return true;
-  });
-  future.then((v) => v);
-  return result;
-}
 
 main(){
-  testDbCommandCreation();  
-  testPingDbCommand();
-  testDropCollectionCommand();
+  group("DBCommand tests:", (){
+    test("testDbCommandCreation",testDbCommandCreation);
+    test("testPingDbCommand",testPingDbCommand);
+  });
 }

tests/DbTest.dart

+#library("dbtest");
 #import("../lib/mongo.dart");
 #import('dart:builtin');
+#import('../third_party/testing/unittest/unittest_vm.dart');
 testDatabaseName(){
   Db db = new Db('db');
   String dbName;
 }
 
 main(){
-  testDatabaseName();
-  testCollectionInfoCursor();
-  testRemove();
+  group("DBCommand tests:", (){
+    test("testDatabaseName",testDatabaseName);
+    test("testCollectionInfoCursor",testCollectionInfoCursor);
+    test("testRemove",testRemove);
+  });  
+  
 }

tests/MCollectionTest.dart

+#library("mcollection");
 #import("../lib/mongo.dart");
 #import("dart:io");
 #import("dart:builtin");
+#import('../third_party/testing/unittest/unittest_vm.dart');
 testCollectionCreation(){
   Db db = new Db('db');
   MCollection collection = db.collection('student');
 }
-testSaveAll(){
-  Db db = new Db('test');
-  db.open();
-  MCollection newColl; 
-  newColl = db.collection('newColl');  
-  print(newColl.fullName());
-  List<Map> docsToInsert  = new List();
-  for (int i = 0; i < 200; i++){
-    docsToInsert.add({"a":i});
-  }
-  newColl.saveAll(docsToInsert);
-}
-testSave(){
-  Db db = new Db('test');
-  db.open();  
-  MCollection newColl = db.collection('newColl1');  
-  print(newColl.fullName());
-  for (int i = 0; i < 5000; i++){
-    newColl.save({"a":i});
-  }
-}
 testEach(){
-  Db db = new Db('test');
+  Db db = new Db('mongo-dart-test');
   db.open();  
   MCollection newColl = db.collection('newColl1');
   int count = 0;
   }).then((v)=>print("Completed. Sum = $sum, count = $count"));
 }
 testFindEachWithThenClause(){
-  Db db = new Db('test');
+  Db db = new Db('mongo-dart-test');
   db.open();  
   MCollection students = db.collection('students');
   students.drop();
   });
 }
 testFindEach(){
-  Db db = new Db('test');
+  Db db = new Db('mongo-dart-test');
   db.open();  
   MCollection students = db.collection('students');
-  students.drop();
+  students.remove();
   students.insertAll(
     [
      {"name":"Vadim","score":4},
      {"name": "Nick", "score": 5}
     ]
   );
-  db.getLastError().then((v){
-  print(v);
-  print("there");  
+  db.getLastError().then((v){   
   int count = 0;
   int sum = 0;
-  students.find().each((v) => print("student: $v")).then((v){
+  students.find().each((v){
+    count++;
+    sum += v["score"];
+  }).then((v){
+    new Expectation(count).equals(3);
+    new Expectation(sum).equals(13);
+//    new Expectation(count).equals(0);
     db.close();
+    callbackDone();
    });
   });
 }
 testDrop(){
-  Db db = new Db('test');
+  Db db = new Db('mongo-dart-test');
   db.open();
-  db.dropCollection("students").then((v)=>print("deleted"));
+  db.dropCollection("students").then((v)=>v);
   db.dropCollection("students").then((v){
     db.close();
   });  
+}  
+
+testSaveWithIntegerId(){
+  Db db = new Db('mongo-dart-test');
+  db.open();  
+  MCollection coll = db.collection('collection-for-save');
+  coll.remove();
+  var id;
+  List toInsert = [
+                   {"_id":1,"name":"a", "value": 10},
+                   {"_id":2,"name":"b", "value": 20},
+                   {"_id":3,"name":"c", "value": 30},
+                   {"_id":4,"name":"d", "value": 40}
+                 ];
+  coll.insertAll(toInsert);
+  coll.findOne({"name":"c"}).chain((v){  
+    new Expectation(v["value"]).equals(30);    
+    return coll.findOne({"_id":3});
+    }).chain((v){  
+    v["value"] = 31;    
+    coll.save(v);
+    return coll.findOne({"_id":3});
+  }).then((v1){
+    new Expectation(v1["value"]).equals(31);    
+    db.close();
+  });      
 }
-main(){
-  //testFindEachWithThenClause();
-  testDrop();
-  testFindEach();
-//  testDrop();
-/*  
-  testFindEach();
-  testFindEachStudent();
-  testCollectionCreation();
-  testSave();
-  testSaveAll();  
-*/  
+testSaveWithObjectId(){
+  Db db = new Db('mongo-dart-test');
+  db.open();  
+  MCollection coll = db.collection('collection-for-save');
+  coll.remove();
+  var id;
+  List toInsert = [
+                   {"name":"a", "value": 10},
+                   {"name":"b", "value": 20},
+                   {"name":"c", "value": 30},
+                   {"name":"d", "value": 40}
+                 ];
+  coll.insertAll(toInsert);
+  coll.findOne({"name":"c"}).chain((v){  
+    new Expectation(v["value"]).equals(30);
+    id = v["_id"]; 
+    return coll.findOne({"_id":id});
+    }).chain((v){
+    new Expectation(v["value"]).equals(30);
+    v["value"] = 31;    
+    coll.save(v);
+    return coll.findOne({"_id":id});
+  }).then((v1){
+    new Expectation(v1["value"]).equals(31);    
+    db.close();
+  });      
+}
+
+
+main(){  
+  group("MCollection tests:", (){
+//    asyncTest("testFindEach",1,testFindEach);
+//    test("testDrop",testDrop);
+//    test("testSaveWithIntegerId",testSaveWithIntegerId);
+    test("testSaveWithObjectId",testSaveWithObjectId());    
+  });
 }

tests/allTests.dart

+#library("all_tests");
+#import("ConnectionTest.dart",prefix:"connection");
+#import("CursorTest.dart",prefix:"cursor");
+#import("DbCommandTest.dart",prefix:"dbcommand");
+#import("MCollectionTest.dart",prefix:"mcollection");
+#import("DbTest.dart",prefix:"dbtest");
+main(){  
+  connection.main();
+  cursor.main();
+  dbcommand.main();
+  mcollection.main();
+  dbtest.main();
+}

third_party/testing/unittest/coverage_controller.js

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Coverage controller logic - used by coverage test harness to embed tests in
+ * DumpRenderTree and extract coverage information.
+ */
+
+var LONG_LINE = 60000;
+
+function onReceive(e) {
+  if (e.data == 'unittest-suite-done') {
+    var s = JSON.stringify(top._$jscoverage);
+    var res = '';
+    // DumpRenderTree has a bug on lines longer than 2^16, so we split them
+    while (s.length > LONG_LINE) {
+      res += s.substr(0, LONG_LINE) + '<br>\n';
+      s = s.substr(LONG_LINE);
+    }
+    res += s;
+    window.document.body.innerHTML = res;
+    window.layoutTestController.notifyDone();
+  }
+}
+
+if (window.layoutTestController) {
+  window.layoutTestController.dumpAsText();
+  window.layoutTestController.waitUntilDone();
+  window.addEventListener("message", onReceive, false);
+}

third_party/testing/unittest/shared.dart

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Description text of the current test group. If multiple groups are nested,
+ * this will contain all of their text concatenated.
+ */
+String _currentGroup = '';
+
+/** Tests executed in this suite. */
+List<TestCase> _tests;
+
+/**
+ * Callback used to run tests. Entrypoints can replace this with their own
+ * if they want.
+ */
+Function _testRunner;
+
+/** Whether this is run within dartium layout tests. */
+bool _isLayoutTest = false;
+
+/** Current test being executed. */
+int _currentTest = 0;
+
+/** Total number of callbacks that have been executed in the current test. */
+int _callbacksCalled = 0;
+
+final _UNINITIALIZED = 0;
+final _READY         = 1;
+final _RUNNING_TEST  = 2;
+
+/**
+ * Whether an undetected error occurred while running the last test. These
+ * errors are commonly caused by DOM callbacks that were not guarded in a
+ * try-catch block.
+ */
+final _UNCAUGHT_ERROR = 3;
+
+int _state = _UNINITIALIZED;
+
+final _PASS  = 'pass';
+final _FAIL  = 'fail';
+final _ERROR = 'error';
+
+/** Creates an expectation for the given value. */
+Expectation expect(value) => new Expectation(value);
+
+/** Evaluates the given function and validates that it throws an exception. */
+void expectThrow(function) {
+  bool threw = false;
+  try {
+    function();
+  } catch (var e) {
+    threw = true;
+  }
+  Expect.equals(true, threw, 'Expected exception but none was thrown.');
+}
+
+/**
+ * Creates a new test case with the given description and body. The
+ * description will include the descriptions of any surrounding group()
+ * calls.
+ */
+void test(String spec, TestFunction body) {
+  _ensureInitialized();
+
+  _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0));
+}
+
+/**
+ * Creates a new async test case with the given description and body. The
+ * description will include the descriptions of any surrounding group()
+ * calls.
+ */
+void asyncTest(String spec, int callbacks, TestFunction body) {
+  _ensureInitialized();
+
+  final testCase = new TestCase(
+      _tests.length + 1, _fullSpec(spec), body, callbacks);
+  _tests.add(testCase);
+
+  if (callbacks < 1) {
+    testCase.error(
+        'Async tests must wait for at least one callback ', '');
+  }
+}
+
+void serialInvokeAsync(List closures) {
+  final length = closures.length;
+  if (length > 0) {
+    int i = 0;
+    void invokeNext() {
+      closures[i]();
+      i++;
+      if (i < length) {
+        window.setTimeout(invokeNext, 0);
+      }
+    }
+    window.setTimeout(invokeNext, 0);
+  }
+}
+
+/**
+ * Creates a new named group of tests. Calls to group() or test() within the
+ * body of the function passed to this will inherit this group's description.
+ */
+void group(String description, void body()) {
+  _ensureInitialized();
+
+  // Concatenate the new group.
+  final oldGroup = _currentGroup;
+  if (_currentGroup != '') {
+    // Add a space.
+    _currentGroup = '$_currentGroup $description';
+  } else {
+    // The first group.
+    _currentGroup = description;
+  }
+
+  try {
+    body();
+  } finally {
+    // Now that the group is over, restore the previous one.
+    _currentGroup = oldGroup;
+  }
+}
+
+/** Called by subclasses to indicate that an asynchronous test completed. */
+void callbackDone() {
+  _callbacksCalled++;
+  final testCase = _tests[_currentTest];
+  if (testCase.callbacks == 0) {
+    testCase.error(
+        "Can't call callbackDone() on a synchronous test", '');
+    _state = _UNCAUGHT_ERROR;
+  } else if (_callbacksCalled > testCase.callbacks) {
+    final expected = testCase.callbacks;
+    testCase.error(
+        'More calls to callbackDone() than expected. '
+        + 'Actual: ${_callbacksCalled}, expected: ${expected}', '');
+    _state = _UNCAUGHT_ERROR;
+  } else if ((_callbacksCalled == testCase.callbacks) &&
+      (_state != _RUNNING_TEST)) {
+    testCase.pass();
+    _currentTest++;
+    _testRunner();
+  }
+}
+
+void forLayoutTests() {
+  _isLayoutTest = true;
+}
+
+/** Runs all queued tests, one at a time. */
+_runTests() {
+  _platformStartTests();
+
+  _platformDefer(() {
+    assert (_currentTest == 0);
+    _testRunner();
+  });
+}
+
+/** Runs a single test. */
+_runTest(TestCase testCase) {
+  try {
+    _callbacksCalled = 0;
+    _state = _RUNNING_TEST;
+
+    testCase.test();
+
+    if (_state != _UNCAUGHT_ERROR) {
+      if (testCase.callbacks == _callbacksCalled) {
+        testCase.pass();
+      }
+    }
+
+  } catch (ExpectException e, var trace) {
+    if (_state != _UNCAUGHT_ERROR) {
+      //TODO(pquitslund) remove guard once dartc reliably propagates traces
+      testCase.fail(e.message, trace == null ? '' : trace.toString());
+    }
+  } catch (var e, var trace) {
+    if (_state != _UNCAUGHT_ERROR) {
+      //TODO(pquitslund) remove guard once dartc reliably propagates traces
+      testCase.error('Caught ${e}', trace == null ? '' : trace.toString());
+    }
+  } finally {
+    _state = _READY;
+  }
+}
+
+/**
+ * Runs a batch of tests, yielding whenever an asynchronous test starts
+ * running. Tests will resume executing when such asynchronous test calls
+ * [done] or if it fails with an exception.
+ */
+_nextBatch() {
+  while (_currentTest < _tests.length) {
+    final testCase = _tests[_currentTest];
+
+    _runTest(testCase);
+
+    if (!testCase.isComplete && testCase.callbacks > 0) return;
+
+    _currentTest++;
+  }
+
+  _completeTests();
+}
+
+/** Publish results on the page and notify controller. */
+_completeTests() {
+  _state = _UNINITIALIZED;
+
+  int testsPassed_ = 0;
+  int testsFailed_ = 0;
+  int testsErrors_ = 0;
+
+  for (TestCase t in _tests) {
+    switch (t.result) {
+      case _PASS:  testsPassed_++; break;
+      case _FAIL:  testsFailed_++; break;
+      case _ERROR: testsErrors_++; break;
+    }
+  }
+
+  _platformCompleteTests(testsPassed_, testsFailed_, testsErrors_);
+}
+
+String _fullSpec(String spec) {
+  if (spec === null) return '$_currentGroup';
+  return _currentGroup != '' ? '$_currentGroup $spec' : spec;
+}
+
+/**
+ * Lazily initializes the test library if not already initialized.
+ */
+_ensureInitialized() {
+  if (_state != _UNINITIALIZED) return;
+
+  _tests = <TestCase>[];
+  _currentGroup = '';
+  _state = _READY;
+  _testRunner = _nextBatch;
+
+  _platformInitialize();
+
+  // Immediately queue the suite up. It will run after a timeout (i.e. after
+  // main() has returned).
+  _platformDefer(_runTests);
+}
+
+/**
+ * Wraps an value and provides an "==" operator that can be used to verify that
+ * the value matches a given expectation.
+ */
+class Expectation {
+  final _value;
+
+  Expectation(this._value);
+
+  /** Asserts that the value is equivalent to [expected]. */
+  void equals(expected) {
+    // Use the type-specialized versions when appropriate to give better
+    // error messages.
+    if (_value is String && expected is String) {
+      Expect.stringEquals(expected, _value);
+    } else if (_value is Map && expected is Map) {
+      Expect.mapEquals(expected, _value);
+    } else if (_value is Set && expected is Set) {
+      Expect.setEquals(expected, _value);
+    } else {
+      Expect.equals(expected, _value);
+    }
+  }
+
+  /**
+   * Asserts that the difference between [expected] and the value is within
+   * [tolerance]. If no tolerance is given, it is assumed to be the value 4
+   * significant digits smaller than the expected value.
+   */
+  void approxEquals(num expected,
+      [num tolerance = null, String reason = null]) {
+    Expect.approxEquals(expected, _value, tolerance: tolerance, reason: reason);
+  }
+
+  /** Asserts that the value is [null]. */
+  void isNull() {
+    Expect.equals(null, _value);
+  }
+
+  /** Asserts that the value is not [null]. */
+  void isNotNull() {
+    Expect.notEquals(null, _value);
+  }
+
+  /** Asserts that the value is [true]. */
+  void isTrue() {
+    Expect.equals(true, _value);
+  }
+
+  /** Asserts that the value is [false]. */
+  void isFalse() {
+    Expect.equals(false, _value);
+  }
+
+  /** Asserts that the value has the same elements as [expected]. */
+  void equalsCollection(Collection expected) {
+    Expect.listEquals(expected, _value);
+  }
+
+  /**
+   * Checks that every element of [expected] is also in [actual], and that
+   * every element of [actual] is also in [expected].
+   */
+  void equalsSet(Iterable expected) {
+    Expect.setEquals(expected, _value);
+  }
+}
+
+/** Summarizes information about a single test case. */
+class TestCase {
+  /** Identifier for this test. */
+  final id;
+
+  /** A description of what the test is specifying. */
+  final String description;
+
+  /** The body of the test case. */
+  final TestFunction test;
+
+  /** Total number of callbacks to wait for before the test completes. */
+  int callbacks;
+
+  /** Error or failure message. */
+  String message  = '';
+
+  /**
+   * One of [_PASS], [_FAIL], or [_ERROR] or [null] if the test hasn't run yet.
+   */
+  String result;
+
+  /** Stack trace associated with this test, or null if it succeeded. */
+  String stackTrace;
+
+  Date startTime;
+
+  Duration runningTime;
+
+  TestCase(this.id, this.description, this.test, this.callbacks);
+
+  bool get isComplete() => result != null;
+
+  void pass() {
+    result = _PASS;
+  }
+
+  void fail(String message_, String stackTrace_) {
+    result = _FAIL;
+    this.message = message_;
+    this.stackTrace = stackTrace_;
+  }
+
+  void error(String message_, String stackTrace_) {
+    result = _ERROR;
+    this.message = message_;
+    this.stackTrace = stackTrace_;
+  }
+}
+
+typedef void TestFunction();

third_party/testing/unittest/test_controller.js

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Test controller logic - used by unit test harness to embed tests in
+ * DumpRenderTree.
+ */
+
+if (navigator.webkitStartDart) {
+  navigator.webkitStartDart();
+}
+
+function processMessage(msg) {
+  if (window.layoutTestController) {
+    if (msg == 'unittest-suite-done') {
+      window.layoutTestController.notifyDone();
+    } else if (msg == 'unittest-suite-wait-for-done') {
+      window.layoutTestController.startedDartTest = true;
+    }
+  }
+}
+
+function onReceive(e) {
+  processMessage(e.data);
+}
+
+if (window.layoutTestController) {
+  window.layoutTestController.dumpAsText();
+  window.layoutTestController.waitUntilDone();
+}
+window.addEventListener("message", onReceive, false);
+
+function onLoad(e) {
+  // needed for dartium compilation errors.
+  if (window.compilationError) {
+    var element = document.createElement('pre');
+    element.innerHTML = window.compilationError;
+    document.body.appendChild(element);
+    window.layoutTestController.notifyDone();
+    return;
+  }
+}
+
+window.addEventListener("DOMContentLoaded", onLoad, false);

third_party/testing/unittest/unittest_dartest.dart

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * A simple unit test library for running tests inside the DARTest In-App test
+ * runner along side the application under test in a browser.
+ */
+#library("unittest");
+
+#import("dart:dom");
+
+#source("shared.dart");
+
+/** Getter so that the DARTest UI can access tests. */
+List<TestCase> get tests() => _tests;
+
+int testsRun = 0;
+int testsFailed = 0;
+int testsErrors = 0;
+
+bool previousAsyncTest = false;
+
+Function updateUI = null;
+
+Function dartestLogger = null;
+
+_platformDefer(void callback()) {
+  _testRunner = runDartests;
+  // DARTest ignores the callback. Tests are launched from UI.
+}
+
+// Update test results
+updateTestStats(TestCase test_) {
+  assert(test_.result != null);
+  if(test_.startTime != null) {
+    test_.runningTime = (new Date.now()).difference(test_.startTime);
+  }
+  testsRun++;
+  switch (test_.result) {
+    case 'fail': testsFailed++; break;
+    case 'error': testsErrors++; break;   
+  } 
+  updateUI(test_);
+}
+
+// Run tests sequentially
+runDartests() {
+  if(previousAsyncTest) {
+    updateTestStats(_tests[_currentTest - 1]);
+    previousAsyncTest = false;
+  }
+  if(_currentTest < _tests.length) {
+    final testCase = _tests[_currentTest];
+    dartestLogger('Running test:' + testCase.description);
+    testCase.startTime = new Date.now();
+    _runTest(testCase);
+    if (!testCase.isComplete && testCase.callbacks > 0) {
+      previousAsyncTest = true;
+      return;
+    }
+    updateTestStats(testCase);
+    _currentTest++;
+    window.setTimeout(runDartests, 0);
+  }
+}
+
+_platformStartTests() {
+  // TODO(shauvik): Support for VM and command line coming soon!
+  window.console.log("Warning: Running DARTest from VM or Command-line.");
+}
+
+_platformInitialize() {
+  // Do nothing
+}
+
+_platformCompleteTests(int testsPassed_, int testsFailed_, int testsErrors_) {
+  // Do nothing
+}
+
+String getTestResultsCsv() {
+  StringBuffer out = new StringBuffer();
+  _tests.forEach((final test_) {
+    String result = 'none';
+    if(test_.result != null) {
+      result = test_.result.toUpperCase();
+    }
+    out.add('${test_.id}, "${test_.description}", $result\n');
+  });
+  return out.toString();
+}

third_party/testing/unittest/unittest_dom.dart

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * A simple unit test library for running tests in a browser.
+ */
+#library("unittest");
+
+#import("dart:dom");
+
+#source("shared.dart");
+
+// TODO(rnystrom): Get rid of this if we get canonical closures for methods.
+EventListener _onErrorClosure;
+
+_platformInitialize() {
+  _onErrorClosure = (e) { _onError(e); };
+}
+
+_platformDefer(void callback()) {
+  window.setTimeout(callback, 0);
+}
+
+void _onError(e) {
+ if (_currentTest < _tests.length) {
+    final testCase = _tests[_currentTest];
+    // TODO(vsm): figure out how to expose the stack trace here
+    // Currently e.message works in dartium, but not in dartc.
+    testCase.error('(DOM callback has errors) Caught ${e}', '');
+    _state = _UNCAUGHT_ERROR;
+    if (testCase.callbacks > 0) {
+      _currentTest++;
+      _testRunner();
+    }
+  }
+}
+
+/** Runs all queued tests, one at a time. */
+_platformStartTests() {
+  window.postMessage('unittest-suite-wait-for-done', '*');
+
+  // Listen for uncaught errors.
+  window.addEventListener('error', _onErrorClosure, true);
+}
+
+_platformCompleteTests(int testsPassed, int testsFailed, int testsErrors) {
+  window.removeEventListener('error', _onErrorClosure);
+
+  if (_isLayoutTest && testsPassed == _tests.length) {
+    document.body.innerHTML = "PASS";
+  } else {
+    var newBody = new StringBuffer();
+    newBody.add("<table class='unittest-table'><tbody>");
+    newBody.add(testsPassed == _tests.length
+        ? "<tr><td colspan='3' class='unittest-pass'>PASS</td></tr>"
+        : "<tr><td colspan='3' class='unittest-fail'>FAIL</td></tr>");
+
+    for (final test_ in _tests) {
+      newBody.add(_toHtml(test_));
+    }
+
+    if (testsPassed == _tests.length) {
+      newBody.add("""
+          <tr><td colspan='3' class='unittest-pass'>
+            All ${testsPassed} tests passed
+          </td></tr>""");
+    } else {
+      newBody.add("""
+          <tr><td colspan='3'>Total
+            <span class='unittest-pass'>${testsPassed} passed</span>,
+            <span class='unittest-fail'>${testsFailed} failed</span>
+            <span class='unittest-error'>${testsErrors} errors</span>
+          </td></tr>""");
+    }
+    newBody.add("</tbody></table>");
+    document.body.innerHTML = newBody.toString();
+  }
+
+  window.postMessage('unittest-suite-done', '*');
+}
+
+String _toHtml(TestCase test_) {
+  if (!test_.isComplete) {
+    return '''
+        <tr>
+          <td>${test_.id}</td>
+          <td class="unittest-error">NO STATUS</td>
+          <td>Test did not complete</td>
+        </tr>''';
+  }
+
+  var html = '''
+      <tr>
+        <td>${test_.id}</td>
+        <td class="unittest-${test_.result}">${test_.result.toUpperCase()}</td>
+        <td>Expectation: ${test_.description}. ${_htmlEscape(test_.message)}</td>
+      </tr>''';
+
+  if (test_.stackTrace != null) {
+    html +=
+        '<tr><td></td><td colspan="2"><pre>${_htmlEscape(test_.stackTrace)}</pre></td></tr>';
+  }
+
+  return html;
+}
+
+//TODO(pquitslund): Move to a common lib
+String _htmlEscape(String string) {
+  return string.replaceAll('&', '&amp;')
+               .replaceAll('<','&lt;')
+               .replaceAll('>','&gt;');
+}

third_party/testing/unittest/unittest_html.dart

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * A simple unit test library for running tests in a browser.
+ */
+#library("unittest");
+
+#import("dart:html");
+
+#source("shared.dart");
+
+// TODO(rnystrom): Get rid of this if we get canonical closures for methods.
+EventListener _onErrorClosure;
+
+_platformInitialize() {
+  _onErrorClosure = (e) { _onError(e); };
+}
+
+_platformDefer(void callback()) {
+  window.setTimeout(callback, 0);
+}
+
+void _onError(e) {
+ if (_currentTest < _tests.length) {
+    final testCase = _tests[_currentTest];
+    // TODO(vsm): figure out how to expose the stack trace here
+    // Currently e.message works in dartium, but not in dartc.
+    testCase.error('(DOM callback has errors) Caught ${e}', '');
+    _state = _UNCAUGHT_ERROR;
+    if (testCase.callbacks > 0) {
+      _currentTest++;
+      _testRunner();
+    }
+  }
+}
+
+/** Runs all queued tests, one at a time. */
+_platformStartTests() {
+  window.postMessage('unittest-suite-wait-for-done', '*');
+
+  // Listen for uncaught errors.
+  window.on.error.add(_onErrorClosure);
+}
+
+_platformCompleteTests(int testsPassed, int testsFailed, int testsErrors) {
+  window.on.error.remove(_onErrorClosure);
+
+  if (_isLayoutTest && testsPassed == _tests.length) {
+    document.body.innerHTML = "PASS";
+  } else {
+    var newBody = new StringBuffer();
+    newBody.add("<table class='unittest-table'><tbody>");
+    newBody.add(testsPassed == _tests.length
+        ? "<tr><td colspan='3' class='unittest-pass'>PASS</td></tr>"
+        : "<tr><td colspan='3' class='unittest-fail'>FAIL</td></tr>");
+
+    for (final test_ in _tests) {
+      newBody.add(_toHtml(test_));
+    }
+
+    if (testsPassed == _tests.length) {
+      newBody.add("""
+          <tr><td colspan='3' class='unittest-pass'>
+            All ${testsPassed} tests passed
+          </td></tr>""");
+    } else {
+      newBody.add("""
+          <tr><td colspan='3'>Total
+            <span class='unittest-pass'>${testsPassed} passed</span>,
+            <span class='unittest-fail'>${testsFailed} failed</span>
+            <span class='unittest-error'>${testsErrors} errors</span>
+          </td></tr>""");
+    }
+    newBody.add("</tbody></table>");
+    document.body.innerHTML = newBody.toString();
+  }
+
+  window.postMessage('unittest-suite-done', '*');
+}
+
+String _toHtml(TestCase test_) {
+  if (!test_.isComplete) {
+    return '''
+        <tr>
+          <td>${test_.id}</td>
+          <td class="unittest-error">NO STATUS</td>
+          <td>Test did not complete</td>
+        </tr>''';
+  }
+
+  var html = '''
+      <tr>
+        <td>${test_.id}</td>
+        <td class="unittest-${test_.result}">${test_.result.toUpperCase()}</td>
+        <td>Expectation: ${test_.description}. ${_htmlEscape(test_.message)}</td>
+      </tr>''';
+
+  if (test_.stackTrace != null) {
+    html +=
+        '<tr><td></td><td colspan="2"><pre>${_htmlEscape(test_.stackTrace)}</pre></td></tr>';
+  }
+
+  return html;
+}
+
+//TODO(pquitslund): Move to a common lib
+String _htmlEscape(String string) {
+  return string.replaceAll('&', '&amp;')
+               .replaceAll('<','&lt;')
+               .replaceAll('>','&gt;');
+}

third_party/testing/unittest/unittest_node.dart

+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * A simple unit test entrypoint for running tests on node compiled with frog.
+ */
+#library("unittest");
+
+#import('../../../frog/lib/node/node.dart');
+
+#source("shared.dart");
+
+_platformInitialize() {
+  // Do nothing.
+}
+
+_platformDefer(void callback()) {
+  setTimeout(callback, 0);
+}
+
+_platformStartTests() {
+  // Do nothing.
+}
+
+_platformCompleteTests(int testsPassed, int testsFailed, int testsErrors) {
+  // Print each test's result.
+  for (final test in _tests) {
+    print('${test.result.toUpperCase()}: ${test.description}');
+
+    if (test.message != '') {
+      print('  ${test.message}');
+    }
+  }
+
+  // Show the summary.
+  print('');
+
+  var success = false;
+  if (testsPassed == 0 && testsFailed == 0 && testsErrors == 0) {
+    print('No tests found.');
+    // This is considered a failure too: if this happens you probably have a
+    // bug in your unit tests.
+  } else if (testsFailed == 0 && testsErrors == 0) {
+    print('All $testsPassed tests passed.');
+    success = true;
+  } else {
+    print('$testsPassed PASSED, $testsFailed FAILED, $testsErrors ERRORS');
+  }
+
+  // A non-zero exit code is used by the test infrastructure to detect failure.
+  if (!success) process.exit(1);
+}

third_party/testing/unittest/unittest_vm.dart

+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * A simple unit test library for running tests on the VM.
+ */
+#library('unittest');
+
+#import('dart:io');
+#source('shared.dart');
+
+_platformInitialize() {
+  // Do nothing.
+}
+
+_platformDefer(void callback()) {
+  new Timer(0, (timer) {
+    callback();
+  });
+}
+
+_platformStartTests() {
+  // Do nothing.
+}
+
+_platformCompleteTests(int testsPassed, int testsFailed, int testsErrors) {
+  // Print each test's result.
+  for (final test in _tests) {
+    print('${test.result.toUpperCase()}: ${test.description}');
+
+    if (test.message != '') {
+      print('  ${test.message}');
+    }
+  }
+
+  // Show the summary.
+  print('');
+
+  var success = false;
+  if (testsPassed == 0 && testsFailed == 0 && testsErrors == 0) {
+    print('No tests found.');
+    // This is considered a failure too: if this happens you probably have a
+    // bug in your unit tests.
+  } else if (testsFailed == 0 && testsErrors == 0) {
+    print('All $testsPassed tests passed.');
+    success = true;
+  } else {
+    print('$testsPassed PASSED, $testsFailed FAILED, $testsErrors ERRORS');
+  }
+
+  // A non-zero exit code is used by the test infrastructure to detect failure.
+  if (!success) exit(1);
+}