MojErr MojDbQuotaTest::testErrors() { #ifdef MOJ_USE_BDB MojRefCountedPtr<MojDbStorageEngine> engine(new MojDbBerkeleyEngine()); #elif MOJ_USE_LDB MojRefCountedPtr<MojDbStorageEngine> engine(new MojDbLevelEngine()); #else MojRefCountedPtr<MojDbStorageEngine> engine; #endif MojAllocCheck(engine.get()); MojRefCountedPtr<MojDbTestStorageEngine> testEngine(new MojDbTestStorageEngine(engine.get())); MojAllocCheck(testEngine.get()); MojErr err = testEngine->open(MojDbTestDir); MojTestErrCheck(err); MojDb db; err = db.open(MojDbTestDir, testEngine.get()); MojTestErrCheck(err); // test that failed put does not affect quota MojInt64 quotaUsage1 = 0; err = getQuotaUsage(db, _T("com.foo.*"), quotaUsage1); MojTestErrCheck(err); err = testEngine->setNextError(_T("txn.commit"), MojErrDbDeadlock); MojTestErrCheck(err); err = put(db, MojTestKind3Objects[1]); MojTestErrExpected(err, MojErrDbDeadlock); MojInt64 quotaUsage2 = 0; err = getQuotaUsage(db, _T("com.foo.*"), quotaUsage2); MojTestErrCheck(err); MojTestAssert(quotaUsage2 == quotaUsage1); // test that failed putQuota has no effect err = testEngine->setNextError(_T("txn.commit"), MojErrDbDeadlock); MojTestErrCheck(err); MojObject obj; err = obj.fromJson(_T("{\"owner\":\"com.foo.boo\",\"size\":1000}")); MojErrCheck(err); err = db.putQuotas(&obj, &obj + 1); MojTestErrExpected(err, MojErrDbDeadlock); MojInt64 quotaUsage3 = 0; err = getQuotaUsage(db, _T("com.foo.*"), quotaUsage3); MojTestErrCheck(err); MojTestAssert(quotaUsage3 == quotaUsage1); // test that failed putKind has no effect err = testEngine->setNextError(_T("txn.commit"), MojErrDbDeadlock); MojTestErrCheck(err); err = obj.fromJson(MojTestKind3Str2); MojTestErrCheck(err); err = db.putKind(obj); MojTestErrExpected(err, MojErrDbDeadlock); MojInt64 quotaUsage4 = 0; err = getQuotaUsage(db, _T("com.foo.*"), quotaUsage4); MojTestErrCheck(err); MojTestAssert(quotaUsage4 == quotaUsage1); err = db.close(); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbQuotaTest::testEnforce(MojDb& db) { // set quota size to current usage MojInt64 quotaUsage1 = 0; MojErr err = getQuotaUsage(db, _T("com.foo.bar"), quotaUsage1); MojTestErrCheck(err); MojObject obj; err = obj.putString(_T("owner"), _T("com.foo.bar")); MojErrCheck(err); err = obj.putInt(_T("size"), quotaUsage1); MojErrCheck(err); err = db.putQuotas(&obj, &obj + 1); MojErrCheck(err); err = put(db, MojTestKind1Objects[3]); MojTestErrExpected(err, MojErrDbQuotaExceeded); // Try to delete the kind MojString kindStr; err = kindStr.assign(_T("Test:1")); MojTestErrCheck(err); bool found = false; err = db.delKind(kindStr, found); //The delete should be failure, because it contain sub kind "Test2:1" MojTestErrExpected(err,MojErrDbKindHasSubKinds); MojTestAssert(!found); return MojErrNone; }
MojErr MojDbPerfUpdateTest::timeUpdateKind(MojDb& db, const MojChar* kindJson, MojObject& kindObj, MojTime& addIndexTime, MojTime& dropIndexTime) { MojTime startTime; MojTime endTime; MojObject origKindObj; MojErr err = origKindObj.fromJson(kindJson); MojTestErrCheck(err); for (MojUInt64 i = 0; i < numUpdateKindIterations; i++) { err = MojGetCurrentTime(startTime); MojTestErrCheck(err); bool found; err = kindObj.del(MojDb::RevKey, found); MojTestErrCheck(err); err = db.putKind(kindObj); MojTestErrCheck(err); err = MojGetCurrentTime(endTime); MojTestErrCheck(err); addIndexTime += (endTime - startTime); totalTestTime += (endTime - startTime); err = MojGetCurrentTime(startTime); MojTestErrCheck(err); err = origKindObj.del(MojDb::RevKey, found); MojTestErrCheck(err); err = db.putKind(origKindObj); MojTestErrCheck(err); err = MojGetCurrentTime(endTime); MojTestErrCheck(err); dropIndexTime += (endTime - startTime); totalTestTime += (endTime - startTime); } return MojErrNone; }
/** * run */ MojErr MojDbShardManagerTest::run() { MojErr err = MojErrNone; MojDb db; // open err = db.open(MojDbTestDir); MojTestErrCheck(err); MojDbShardEngine* p_eng = db.shardEngine(); MojDbShardIdCache cache; err = testShardIdCacheIndexes(&cache); MojTestErrCheck(err); err = testShardIdCacheOperations(&cache); MojTestErrCheck(err); err = testShardEngine(p_eng); MojTestErrCheck(err); err = testShardCreateAndRemoveWithRecords(db); MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); return err; }
MojErr MojDbQuotaTest::testEnforce(MojDb& db) { // set quota size to current usage MojInt64 quotaUsage1 = 0; MojErr err = getQuotaUsage(db, _T("com.foo.bar"), quotaUsage1); MojTestErrCheck(err); MojObject obj; err = obj.putString(_T("owner"), _T("com.foo.bar")); MojErrCheck(err); err = obj.putInt(_T("size"), quotaUsage1); MojErrCheck(err); err = db.putQuotas(&obj, &obj + 1); MojErrCheck(err); err = put(db, MojTestKind1Objects[3]); MojTestErrExpected(err, MojErrDbQuotaExceeded); // make sure we can delete the kind MojString kindStr; err = kindStr.assign(_T("Test:1")); MojTestErrCheck(err); bool found = false; err = db.delKind(kindStr, found); MojTestErrCheck(err); MojTestAssert(found); return MojErrNone; }
MojErr MojDbPermissionTest::testAdminPermissions(MojDb& db) { MojObject permission; MojErr err = permission.fromJson(MojTestAdminPermission1); MojTestErrCheck(err); err = db.putPermissions(&permission, &permission + 1); MojTestErrCheck(err); MojDbReq reqAdmin(false); err = reqAdmin.domain(_T("com.admin")); MojTestErrCheck(err); // put kind with non-matching owner with admin role MojObject kind; err = kind.fromJson(MojTestKind1); MojTestErrCheck(err); err = db.putKind(kind, MojDb::FlagNone, reqAdmin); MojTestErrCheck(err); err = kind.fromJson(MojTestKind2); MojTestErrCheck(err); err = db.putKind(kind, MojDb::FlagNone, reqAdmin); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbPermissionTest::run() { MojDb db; MojObject conf; MojErr err = conf.fromJson(MojTestConf); MojTestErrCheck(err); err = db.configure(conf); MojErrCheck(err); err = db.open(MojDbTestDir); MojTestErrCheck(err); err = testInvalidPermissions(db); MojTestErrCheck(err); err = testAdminPermissions(db); MojTestErrCheck(err); err = testKindPermissions(db); MojTestErrCheck(err); err = testObjectPermissions(db); MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbPermissionTest::testKindPermissions(MojDb& db) { MojObject kind; MojErr err = kind.fromJson(MojTestKind1); MojTestErrCheck(err); // new kind, mismatched owner and caller MojDbReq req(false); err = req.domain(_T("com.bar")); MojTestErrCheck(err); err = db.putKind(kind, MojDb::FlagNone, req); MojTestErrExpected(err, MojErrDbPermissionDenied); // new kind, matched owner and caller err = req.domain(_T("com.foo")); MojTestErrCheck(err); err = db.putKind(kind, MojDb::FlagNone, req); MojTestErrCheck(err); // existing kind, matched owner and caller err = db.putKind(kind, MojDb::FlagNone, req); MojTestErrCheck(err); // existing kind, mismatched owner and caller err = req.domain(_T("com.bar")); MojTestErrCheck(err); err = db.putKind(kind, MojDb::FlagNone, req); MojTestErrExpected(err, MojErrDbPermissionDenied); // delKind, mismatched owner and caller MojString id; err = id.assign(_T("PermissionTest:1")); MojTestErrCheck(err); bool found = false; err = db.delKind(id, found, MojDb::FlagNone, req); MojTestErrExpected(err, MojErrDbPermissionDenied); return MojErrNone; }
MojErr MojDbQuotaTest::run() { MojDb db; MojErr err = db.open(MojDbTestDir); MojTestErrCheck(err); MojObject obj; err = obj.fromJson(MojTestKind1Str1); MojTestErrCheck(err); err = db.putKind(obj); MojTestErrCheck(err); err = testUsage(db); MojTestErrCheck(err); err = testMultipleQuotas(db); MojTestErrCheck(err); err = testEnforce(db); MojTestErrCheck(err); err = db.close(); MojErrCheck(err); err = testErrors(); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbDistinctTest::run() { // TODO : description, youngseung.ji MojDb db; MojErr err = db.open(MojDbTestDir); MojTestErrCheck(err); // add kind MojObject kindObj; err = kindObj.fromJson(MojDistinctKindStr); MojTestErrCheck(err); err = db.putKind(kindObj); MojTestErrCheck(err); // put test objects for (MojSize i = 0; i < sizeof(MojDistinctTestObjects) / sizeof(MojChar*); ++i) { MojObject obj; err = obj.fromJson(MojDistinctTestObjects[i]); MojTestErrCheck(err); err = db.put(obj); MojTestErrCheck(err); } // Start testing err = simpleTest(db); MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbQuotaTest::getKindUsage(MojDb& db, const MojChar* kindId, MojInt64& usageOut) { MojRefCountedPtr<MojDbStorageTxn> txn; MojErr err = db.storageEngine()->beginTxn(txn); MojTestErrCheck(err); err = db.quotaEngine()->kindUsage(kindId, usageOut, txn.get()); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbPurgeTest::checkObjectsPurged(MojDb& db, const MojUInt32& count, const MojSize& expectedCount, const MojSize& expectedNumObjects, const MojSize& expectedNumRevTimestampObjects, const MojObject& expectedLastPurgeRevNum) { //check number of objects purged MojTestAssert(count == expectedCount); //there should still be expectedNumObjects test objects MojDbQuery query; MojErr err = query.from(_T("PurgeTest:1")); MojTestErrCheck(err); MojDbCursor cursor; err = db.find(query, cursor); MojTestErrCheck(err); MojUInt32 objCount; err = cursor.count(objCount); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(objCount == expectedNumObjects); //there should be expectedNumRevTimestampObjects RevTimestamp objects MojDbQuery revQuery; err = revQuery.from(MojDbKindEngine::RevTimestampId); MojTestErrCheck(err); MojDbCursor revCursor; err = db.find(revQuery, revCursor); MojTestErrCheck(err); MojUInt32 revTimestampObjCount; err = revCursor.count(revTimestampObjCount); MojTestErrCheck(err); err = revCursor.close(); MojTestErrCheck(err); MojTestAssert(revTimestampObjCount == expectedNumRevTimestampObjects); //lastPurgedRevNum should be equal to the expectedLastPurgeRevNum MojObject revNum; err = db.purgeStatus(revNum); MojTestErrCheck(err); MojTestAssert(revNum == expectedLastPurgeRevNum); return MojErrNone; }
MojErr MojDbWatchTest::cancelTest(MojDb& db) { // cancel find MojDbQuery query; MojErr err = query.from(_T("WatchTest:1")); MojTestErrCheck(err); err = query.where(_T("foo"), MojDbQuery::OpLessThanEq, 45); MojTestErrCheck(err); MojRefCountedPtr<TestWatcher> watcher(new TestWatcher); MojTestAssert(watcher.get()); watcher->m_slot.cancel(); MojDbCursor cursor; err = db.find(query, cursor, watcher->m_slot); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); watcher->m_slot.cancel(); MojTestAssert(watcher->m_count == 0); MojObject id; err = put(db, 1, 1, id, m_rev); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); // cancel watch watcher.reset(new TestWatcher); MojTestAssert(watcher.get()); MojDbQuery queryWithRev; err = queryWithRev.from(_T("WatchTest:1")); MojTestErrCheck(err); err = queryWithRev.where(_T("foo"), MojDbQuery::OpEq, 45); MojTestErrCheck(err); err = queryWithRev.where(_T("_rev"), MojDbQuery::OpGreaterThan, m_rev); MojTestErrCheck(err); bool fired = false; err = db.watch(queryWithRev, cursor, watcher->m_slot, fired); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(!fired); MojTestAssert(watcher->m_count == 0); watcher->m_slot.cancel(); MojTestAssert(watcher->m_count == 0); err = put(db, 45, 45, id, m_rev); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); return MojErrNone; }
MojErr MojDbPerfCreateTest::putLargeArrayObj(MojDb& db, const MojChar* kindId, MojUInt64& lgArrayObjTime) { timespec startTime; startTime.tv_nsec = 0; startTime.tv_sec = 0; timespec endTime; endTime.tv_nsec = 0; endTime.tv_sec = 0; for (MojUInt64 i = 0; i < numInsert; i++) { MojObject obj; MojErr err = obj.putString(MojDb::KindKey, kindId); MojTestErrCheck(err); err = createLargeArrayObj(obj, i); MojTestErrCheck(err); clock_gettime(CLOCK_REALTIME, &startTime); err = db.put(obj); MojTestErrCheck(err); clock_gettime(CLOCK_REALTIME, &endTime); lgArrayObjTime += timeDiff(startTime, endTime); totalTestTime += timeDiff(startTime, endTime); } return MojErrNone; }
/** * verifyRecords */ MojErr MojDbShardManagerTest::verifyRecords (const MojChar* strKind, MojDb& db, const MojDbShardInfo&, MojUInt32& count) { MojDbQuery query; MojDbCursor cursor; count = 0; MojErr err = query.from(strKind); MojErrCheck(err); err = db.find(query, cursor); MojErrCheck(err); while (true) { bool found; MojObject dbObj; err = cursor.get(dbObj, found); MojErrCheck(err); if (!found) break; ++count; } return MojErrNone; }
/** * is kind exist? */ MojErr MojDbShardManagerTest::verifyKindExistance (MojString kindId, MojDb& db) { bool foundOurKind = false; MojString str; //for debug //kinds map MojDbKindEngine::KindMap& map = db.kindEngine()->kindMap(); for (MojDbKindEngine::KindMap::ConstIterator it = map.begin(); it != map.end(); ++it) { str = it.key(); if(kindId == str) { foundOurKind = true; break; } } if (!foundOurKind) MojErrThrowMsg(MojErrDbKindNotRegistered, "Kind %s not found in kindMap", kindId.data()); return MojErrNone; }
MojErr MojDbPerfCreateTest::batchPutLargeArrayObj(MojDb& db, const MojChar* kindId, MojUInt64& lgArrayObjTime) { timespec startTime; startTime.tv_nsec = 0; startTime.tv_sec = 0; timespec endTime; endTime.tv_nsec = 0; endTime.tv_sec = 0; MojObject objArray; for (MojUInt64 i = 0; i < numInsert; i++) { MojObject obj; MojErr err = obj.putString(MojDb::KindKey, kindId); MojTestErrCheck(err); err = createLargeArrayObj(obj, i); MojTestErrCheck(err); err = objArray.push(obj); MojTestErrCheck(err); } MojObject::ArrayIterator begin; MojErr err = objArray.arrayBegin(begin); MojTestErrCheck(err); MojObject::ConstArrayIterator end = objArray.arrayEnd(); clock_gettime(CLOCK_REALTIME, &startTime); err = db.put(begin, end); MojTestErrCheck(err); clock_gettime(CLOCK_REALTIME, &endTime); lgArrayObjTime += timeDiff(startTime, endTime); totalTestTime += timeDiff(startTime, endTime); return MojErrNone; }
MojErr MojDbPerfCreateTest::batchPutLargeArrayObj(MojDb& db, const MojChar* kindId, MojTime& lgArrayObjTime) { MojTime startTime; MojTime endTime; MojObject objArray; for (MojUInt64 i = 0; i < numInsert; i++) { MojObject obj; MojErr err = obj.putString(MojDb::KindKey, kindId); MojTestErrCheck(err); err = createLargeArrayObj(obj, i); MojTestErrCheck(err); err = objArray.push(obj); MojTestErrCheck(err); } MojObject::ArrayIterator begin; MojErr err = objArray.arrayBegin(begin); MojTestErrCheck(err); MojObject::ConstArrayIterator end = objArray.arrayEnd(); err = MojGetCurrentTime(startTime); MojTestErrCheck(err); err = db.put(begin, end); MojTestErrCheck(err); err = MojGetCurrentTime(endTime); MojTestErrCheck(err); lgArrayObjTime += (endTime - startTime); totalTestTime += (endTime - startTime); return MojErrNone; }
void SetUp() { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); path = std::string(tempFolder) + '/' + test_info->test_case_name() + '-' + test_info->name(); // open MojAssertNoErr( db.open(path.c_str()) ); // add type MojObject obj; MojAssertNoErr( obj.fromJson(MojKindStr) ); MojAssertNoErr( db.putKind(obj) ); }
MojErr MojDbQuotaTest::getQuotaUsage(MojDb& db, const MojChar* owner, MojInt64& usageOut) { MojInt64 size; MojErr err = db.quotaEngine()->quotaUsage(owner, size, usageOut); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbPermissionTest::testObjectPermissions(MojDb& db) { MojErr err = putPermissions(db); MojTestErrCheck(err); err = checkPermissions(db); MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); err = db.open(MojDbTestDir); MojTestErrCheck(err); err = checkPermissions(db); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDbQuotaTest::put(MojDb& db, const MojChar* objJson) { MojObject obj; MojErr err = obj.fromJson(objJson); MojTestErrCheck(err); err = db.put(obj); MojErrCheck(err); return MojErrNone; }
void deleteMark(MojUInt32 expect = 50ul, int mark = -1, MojDbReqRef req = MojDbReq()) { MojDbQuery query; MojAssertNoErr( query.from(_T("Test:1")) ); MojAssertNoErr( query.where(_T("bar"), MojDbQuery::OpEq, mark) ); MojUInt32 count = 0xbaddcafe; MojAssertNoErr( db.del(query, count, MojDb::FlagNone, req) ); EXPECT_EQ( expect, count ); }
/** * addKind */ MojErr MojDbShardManagerTest::addKind (const MojChar* strKind, MojDb& db) { MojObject kind; MojErr err = kind.fromJson(strKind); MojErrCheck(err); err = db.putKind(kind); MojErrCheck(err); return MojErrNone; }
MojErr MojDbPermissionTest::checkInvalid(const MojChar* json, MojDb& db) { MojObject permission; MojErr err = permission.fromJson(json); MojTestErrCheck(err); err = db.putPermissions(&permission, &permission+1); MojTestErrExpected(err, MojErrDbInvalidCaller); return MojErrNone; }
MojErr MojDbWatchTest::rangeTest(MojDb& db) { MojDbQuery query; MojErr err = query.from(_T("WatchTest:1")); MojTestErrCheck(err); err = query.where(_T("foo"), MojDbQuery::OpGreaterThan, 5); MojTestErrCheck(err); err = query.where(_T("foo"), MojDbQuery::OpLessThan, 100); MojTestErrCheck(err); MojRefCountedPtr<TestWatcher> watcher(new TestWatcher); MojTestAssert(watcher.get()); MojDbCursor cursor; err = db.find(query, cursor, watcher->m_slot); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); MojObject id; MojInt64 rev; err = put(db, 5, 5, id, rev); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); err = put(db, 100, 100, id, rev); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); err = put(db, 6, 6, id, rev); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 1); watcher.reset(new TestWatcher); MojTestAssert(watcher.get()); err = db.find(query, cursor, watcher->m_slot); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); err = put(db, 99, 99, id, rev); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 1); return MojErrNone; }
/** * Add records to first shard for a single Kind */ MojErr MojDbShardManagerTest::createShardObjects1 (MojDb& db, MojDbShardInfo& shard) { MojObject objKind; MojString kindId; MojErr err = kindId.assign(_T("TestShard1:1")); MojErrCheck(err); err = objKind.putString(_T("_kind"), kindId.data()); MojErrCheck(err); //generate err = generateItem(shard); MojErrCheck(err); err = addKind(TestShardKind1Str, db); MojErrCheck(err); err = verifyKindExistance(kindId, db); MojErrCheck(err); //store shard info err = db.shardEngine()->put(shard); MojErrCheck(err); //add record MojObject record; err = record.putString(_T("_kind"), kindId.data()); MojErrCheck(err); //add value MojObject objId(static_cast<MojInt32>(shard.id)); err = record.put(_T("recId"), objId); MojErrCheck(err); //put MojString strShardId; MojDbShardEngine::convertId(shard.id, strShardId); err = db.put(record, MojDb::FlagNone, MojDbReq(), strShardId); MojErrCheck(err); return MojErrNone; }
void buildSample() { for (int i = 0; i < 100; ++i) { MojObject obj; MojAssertNoErr( obj.putString(MojDb::KindKey, _T("Test:1")) ); MojAssertNoErr( obj.put(_T("foo"), (i + 25) % 100) ); MojAssertNoErr( obj.put(_T("bar"), i % 3) ); MojExpectNoErr( db.put(obj) ) << "Failed to put record #" << i; } // db: x0 = (25, 0), (26, 1), (27, 2), (28, 0) .. x74 = (99,2), x75 = (0,0) .. x99 = (24,0) }
void checkMarkWithUpdate(MojUInt32 expect = 50ul, int mark = -1, MojDbReqRef req = MojDbReq()) { MojDbQuery query; MojAssertNoErr( query.from(_T("Test:1")) ); MojAssertNoErr( query.where(_T("bar"), MojDbQuery::OpEq, mark) ); MojObject update; MojUInt32 count = (MojUInt32)(-1); MojAssertNoErr( db.merge(query, MojObject(), count, MojDb::FlagNone, req) ); EXPECT_EQ( expect, count ); }
MojErr MojDbPerfUpdateTest::run() { MojErr err = file.open(UpdateTestFileName, MOJ_O_RDWR | MOJ_O_CREAT | MOJ_O_TRUNC, MOJ_S_IRUSR | MOJ_S_IWUSR); MojTestErrCheck(err); MojString buf; err = buf.format("MojoDb Update Performance Test,,,,,\n\nOperation,Kind,Total Time,Time Per Iteration,Time Per Object\n"); MojTestErrCheck(err); err = fileWrite(file, buf); MojTestErrCheck(err); MojDb db; err = db.open(MojDbTestDir); MojTestErrCheck(err); err = testPut(db); MojTestErrCheck(err); err = testMerge(db); MojTestErrCheck(err); err = testUpdateKind(db); MojTestErrCheck(err); err = MojPrintF("\n\n TOTAL TEST TIME: %llu microseconds\n\n", totalTestTime.microsecs()); MojTestErrCheck(err); err = MojPrintF("\n-------\n"); MojTestErrCheck(err); err = buf.format("\n\nTOTAL TEST TIME,,%llu,,,", totalTestTime.microsecs()); MojTestErrCheck(err); err = fileWrite(file, buf); MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); err = file.close(); MojTestErrCheck(err); return MojErrNone; }