int getRandomNodeId(NdbRestarter& _restarter) { myRandom48Init((long)NdbTick_CurrentMillisecond()); int randomId = myRandom48(_restarter.getNumDbNodes()); int nodeId = _restarter.getDbNodeId(randomId); return nodeId; }
int runBug15587(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int records = ctx->getNumRecords(); NdbRestarter restarter; Uint32 tableId = ctx->getTab()->getTableId(); int dump[2] = { DumpStateOrd::LqhErrorInsert5042, 0 }; dump[1] = tableId; int nodeId = restarter.getDbNodeId(1); ndbout << "Restart node " << nodeId << endl; if (restarter.restartOneDbNode(nodeId, /** initial */ false, /** nostart */ true, /** abort */ true)) return NDBT_FAILED; if (restarter.waitNodesNoStart(&nodeId, 1)) return NDBT_FAILED; int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 }; if (restarter.dumpStateOneNode(nodeId, val2, 2)) return NDBT_FAILED; if (restarter.dumpStateOneNode(nodeId, dump, 2)) return NDBT_FAILED; if (restarter.startNodes(&nodeId, 1)) return NDBT_FAILED; restarter.waitNodesStartPhase(&nodeId, 1, 3); if (restarter.waitNodesNoStart(&nodeId, 1)) return NDBT_FAILED; if (restarter.dumpStateOneNode(nodeId, val2, 1)) return NDBT_FAILED; if (restarter.startNodes(&nodeId, 1)) return NDBT_FAILED; if (restarter.waitNodesStarted(&nodeId, 1)) return NDBT_FAILED; ctx->stopTest(); return NDBT_OK; }
int get50PercentOfNodes(NdbRestarter& restarter, int * _nodes){ // For now simply return all nodes with even node id // TODO Check nodegroup and return one node from each int num50Percent = restarter.getNumDbNodes() / 2; require(num50Percent <= MAX_NDB_NODES); // Calculate which nodes to stop, select all even nodes for (int i = 0; i < num50Percent; i++){ _nodes[i] = restarter.getDbNodeId(i*2); } return num50Percent; }
int runBug29364(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int records = ctx->getNumRecords(); NdbRestarter restarter; Ndb* pNdb = GETNDB(step); HugoTransactions hugoTrans(*ctx->getTab()); if (restarter.getNumDbNodes() < 4) return NDBT_OK; int dump0[] = { 9000, 0 } ; int dump1[] = { 9001, 0 } ; Uint32 ownNode = refToNode(pNdb->getReference()); dump0[1] = ownNode; for (; loops; loops --) { int node0 = restarter.getDbNodeId(rand() % restarter.getNumDbNodes()); int node1 = restarter.getRandomNodeOtherNodeGroup(node0, rand()); restarter.restartOneDbNode(node0, false, true, true); restarter.waitNodesNoStart(&node0, 1); restarter.startNodes(&node0, 1); restarter.waitClusterStarted(); restarter.restartOneDbNode(node1, false, true, true); restarter.waitNodesNoStart(&node1, 1); if (restarter.dumpStateOneNode(node1, dump0, 2)) return NDBT_FAILED; restarter.startNodes(&node1, 1); do { for (Uint32 i = 0; i < 100; i++) { hugoTrans.pkReadRecords(pNdb, 100, 1, NdbOperation::LM_CommittedRead); } } while (restarter.waitClusterStarted(5) != 0); if (restarter.dumpStateOneNode(node1, dump1, 1)) return NDBT_FAILED; } return NDBT_OK; }
int runBug20185(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int records = ctx->getNumRecords(); NdbRestarter restarter; HugoOperations hugoOps(*ctx->getTab()); Ndb* pNdb = GETNDB(step); int dump[] = { 7090, 20 } ; if (restarter.dumpStateAllNodes(dump, 2)) return NDBT_FAILED; NdbSleep_MilliSleep(3000); if(hugoOps.startTransaction(pNdb) != 0) return NDBT_FAILED; if(hugoOps.pkUpdateRecord(pNdb, 1, 1) != 0) return NDBT_FAILED; if (hugoOps.execute_NoCommit(pNdb) != 0) return NDBT_FAILED; int nodeId; const int node = hugoOps.getTransaction()->getConnectedNodeId(); do { nodeId = restarter.getDbNodeId(rand() % restarter.getNumDbNodes()); } while (nodeId == node); if (restarter.insertErrorInAllNodes(7030)) return NDBT_FAILED; if (restarter.insertErrorInNode(nodeId, 7031)) return NDBT_FAILED; NdbSleep_MilliSleep(500); if (hugoOps.execute_Commit(pNdb) == 0) return NDBT_FAILED; NdbSleep_MilliSleep(3000); restarter.waitClusterStarted(); if (restarter.dumpStateAllNodes(dump, 1)) return NDBT_FAILED; return NDBT_OK; }
int runBug15632(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int records = ctx->getNumRecords(); NdbRestarter restarter; int nodeId = restarter.getDbNodeId(1); ndbout << "Restart node " << nodeId << endl; if (restarter.restartOneDbNode(nodeId, /** initial */ false, /** nostart */ true, /** abort */ true)) return NDBT_FAILED; if (restarter.waitNodesNoStart(&nodeId, 1)) return NDBT_FAILED; if (restarter.insertErrorInNode(nodeId, 7165)) return NDBT_FAILED; if (restarter.startNodes(&nodeId, 1)) return NDBT_FAILED; if (restarter.waitNodesStarted(&nodeId, 1)) return NDBT_FAILED; if (restarter.restartOneDbNode(nodeId, /** initial */ false, /** nostart */ true, /** abort */ true)) return NDBT_FAILED; if (restarter.waitNodesNoStart(&nodeId, 1)) return NDBT_FAILED; if (restarter.insertErrorInNode(nodeId, 7171)) return NDBT_FAILED; if (restarter.startNodes(&nodeId, 1)) return NDBT_FAILED; if (restarter.waitNodesStarted(&nodeId, 1)) return NDBT_FAILED; ctx->stopTest(); return NDBT_OK; }
int runDirtyRead(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int records = ctx->getNumRecords(); NdbRestarter restarter; HugoOperations hugoOps(*ctx->getTab()); Ndb* pNdb = GETNDB(step); int i = 0; while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){ g_info << i << ": "; int id = i % restarter.getNumDbNodes(); int nodeId = restarter.getDbNodeId(id); ndbout << "Restart node " << nodeId << endl; restarter.insertErrorInNode(nodeId, 5041); restarter.insertErrorInAllNodes(8048 + (i & 1)); for(int j = 0; j<records; j++){ if(hugoOps.startTransaction(pNdb) != 0) return NDBT_FAILED; if(hugoOps.pkReadRecord(pNdb, j, 1, NdbOperation::LM_CommittedRead) != 0) goto err; int res; if((res = hugoOps.execute_Commit(pNdb)) == 4119) goto done; if(res != 0) goto err; if(hugoOps.closeTransaction(pNdb) != 0) return NDBT_FAILED; } done: if(hugoOps.closeTransaction(pNdb) != 0) return NDBT_FAILED; i++; restarter.waitClusterStarted(60) ; } return result; err: hugoOps.closeTransaction(pNdb); return NDBT_FAILED; }
int runRestarter(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int sync_threads = ctx->getProperty("SyncThreads", (unsigned)0); NdbRestarter restarter; int i = 0; int lastId = 0; if (restarter.getNumDbNodes() < 2){ ctx->stopTest(); return NDBT_OK; } if(restarter.waitClusterStarted(60) != 0){ g_err << "Cluster failed to start" << endl; return NDBT_FAILED; } loops *= restarter.getNumDbNodes(); while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){ int id = lastId % restarter.getNumDbNodes(); int nodeId = restarter.getDbNodeId(id); ndbout << "Restart node " << nodeId << endl; if(restarter.restartOneDbNode(nodeId, false, false, true) != 0){ g_err << "Failed to restartNextDbNode" << endl; result = NDBT_FAILED; break; } if(restarter.waitClusterStarted(60) != 0){ g_err << "Cluster failed to start" << endl; result = NDBT_FAILED; break; } ctx->sync_up_and_wait("PauseThreads", sync_threads); lastId++; i++; } ctx->stopTest(); return result; }
int runBug16772(NDBT_Context* ctx, NDBT_Step* step){ NdbRestarter restarter; if (restarter.getNumDbNodes() < 2) { ctx->stopTest(); return NDBT_OK; } int aliveNodeId = restarter.getRandomNotMasterNodeId(rand()); int deadNodeId = aliveNodeId; while (deadNodeId == aliveNodeId) deadNodeId = restarter.getDbNodeId(rand() % restarter.getNumDbNodes()); if (restarter.insertErrorInNode(aliveNodeId, 930)) return NDBT_FAILED; if (restarter.restartOneDbNode(deadNodeId, /** initial */ false, /** nostart */ true, /** abort */ true)) return NDBT_FAILED; if (restarter.waitNodesNoStart(&deadNodeId, 1)) return NDBT_FAILED; if (restarter.startNodes(&deadNodeId, 1)) return NDBT_FAILED; // It should now be hanging since we throw away NDB_FAILCONF int ret = restarter.waitNodesStartPhase(&deadNodeId, 1, 3, 10); // So this should fail...i.e it should not reach startphase 3 // Now send a NDB_FAILCONF for deadNo int dump[] = { 7020, 323, 252, 0 }; dump[3] = deadNodeId; if (restarter.dumpStateOneNode(aliveNodeId, dump, 4)) return NDBT_FAILED; if (restarter.waitNodesStarted(&deadNodeId, 1)) return NDBT_FAILED; return ret ? NDBT_OK : NDBT_FAILED; }
int runRestarter(NDBT_Context* ctx, NDBT_Step* step) { int result = NDBT_OK; int loops = ctx->getNumLoops(); int waitprogress = ctx->getProperty("WaitProgress", (unsigned)0); int randnode = ctx->getProperty("RandNode", (unsigned)0); NdbRestarter restarter; int i = 0; int lastId = 0; if (restarter.getNumDbNodes() < 2){ ctx->stopTest(); return NDBT_OK; } if(restarter.waitClusterStarted() != 0){ g_err << "Cluster failed to start" << endl; return NDBT_FAILED; } loops *= (restarter.getNumDbNodes() > 2 ? 2 : restarter.getNumDbNodes()); if (loops < restarter.getNumDbNodes()) loops = restarter.getNumDbNodes(); NdbSleep_MilliSleep(200); Uint32 running = ctx->getProperty("Running", (Uint32)0); while (running == 0 && !ctx->isTestStopped()) { NdbSleep_MilliSleep(100); running = ctx->getProperty("Running", (Uint32)0); } if (ctx->isTestStopped()) return NDBT_FAILED; while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){ int id = lastId % restarter.getNumDbNodes(); if (randnode == 1) { id = rand() % restarter.getNumDbNodes(); } int nodeId = restarter.getDbNodeId(id); ndbout << "Restart node " << nodeId << endl; if(restarter.restartOneDbNode(nodeId, false, true, true) != 0){ g_err << "Failed to restartNextDbNode" << endl; result = NDBT_FAILED; break; } if (restarter.waitNodesNoStart(&nodeId, 1)) { g_err << "Failed to waitNodesNoStart" << endl; result = NDBT_FAILED; break; } if (waitprogress) { Uint32 maxwait = 30; ndbout_c("running: 0x%.8x", running); for (Uint32 checks = 0; checks < 3 && !ctx->isTestStopped(); checks++) { ctx->setProperty("Running", (Uint32)0); for (; maxwait != 0 && !ctx->isTestStopped(); maxwait--) { if ((ctx->getProperty("Running", (Uint32)0) & running) == running) goto ok; NdbSleep_SecSleep(1); } if (ctx->isTestStopped()) { g_err << "Test stopped while waiting for progress!" << endl; return NDBT_FAILED; } g_err << "No progress made!!" << endl; return NDBT_FAILED; ok: g_err << "Progress made!! " << endl; } } if (restarter.startNodes(&nodeId, 1)) { g_err << "Failed to start node" << endl; result = NDBT_FAILED; break; } if(restarter.waitClusterStarted() != 0){ g_err << "Cluster failed to start" << endl; result = NDBT_FAILED; break; } if (waitprogress) { Uint32 maxwait = 30; ndbout_c("running: 0x%.8x", running); for (Uint32 checks = 0; checks < 3 && !ctx->isTestStopped(); checks++) { ctx->setProperty("Running", (Uint32)0); for (; maxwait != 0 && !ctx->isTestStopped(); maxwait--) { if ((ctx->getProperty("Running", (Uint32)0) & running) == running) goto ok2; NdbSleep_SecSleep(1); } if (ctx->isTestStopped()) { g_err << "Test stopped while waiting for progress!" << endl; return NDBT_FAILED; } g_err << "No progress made!!" << endl; return NDBT_FAILED; ok2: g_err << "Progress made!! " << endl; ctx->setProperty("Running", (Uint32)0); } } lastId++; i++; } ctx->stopTest(); return result; }
int runBug18612SR(NDBT_Context* ctx, NDBT_Step* step){ // Assume two replicas NdbRestarter restarter; if (restarter.getNumDbNodes() < 2) { ctx->stopTest(); return NDBT_OK; } Uint32 cnt = restarter.getNumDbNodes(); for(int loop = 0; loop < ctx->getNumLoops(); loop++) { int partition0[256]; int partition1[256]; bzero(partition0, sizeof(partition0)); bzero(partition1, sizeof(partition1)); Bitmask<4> nodesmask; Uint32 node1 = restarter.getDbNodeId(rand()%cnt); for (Uint32 i = 0; i<cnt/2; i++) { do { int tmp = restarter.getRandomNodeOtherNodeGroup(node1, rand()); if (tmp == -1) break; node1 = tmp; } while(nodesmask.get(node1)); partition0[i] = node1; partition1[i] = restarter.getRandomNodeSameNodeGroup(node1, rand()); ndbout_c("nodes %d %d", node1, partition1[i]); assert(!nodesmask.get(node1)); assert(!nodesmask.get(partition1[i])); nodesmask.set(node1); nodesmask.set(partition1[i]); } ndbout_c("done"); if (restarter.restartAll(false, true, false)) return NDBT_FAILED; int dump[255]; dump[0] = 9000; memcpy(dump + 1, partition0, sizeof(int)*cnt/2); for (Uint32 i = 0; i<cnt/2; i++) if (restarter.dumpStateOneNode(partition1[i], dump, 1+cnt/2)) return NDBT_FAILED; dump[0] = 9000; memcpy(dump + 1, partition1, sizeof(int)*cnt/2); for (Uint32 i = 0; i<cnt/2; i++) if (restarter.dumpStateOneNode(partition0[i], dump, 1+cnt/2)) return NDBT_FAILED; int val2[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 }; if (restarter.dumpStateAllNodes(val2, 2)) return NDBT_FAILED; if (restarter.insertErrorInAllNodes(932)) return NDBT_FAILED; if (restarter.startAll()) return NDBT_FAILED; if (restarter.waitClusterStartPhase(2)) return NDBT_FAILED; dump[0] = 9001; for (Uint32 i = 0; i<cnt/2; i++) if (restarter.dumpStateAllNodes(dump, 2)) return NDBT_FAILED; if (restarter.waitClusterNoStart(30)) if (restarter.waitNodesNoStart(partition0, cnt/2, 10)) if (restarter.waitNodesNoStart(partition1, cnt/2, 10)) return NDBT_FAILED; if (restarter.startAll()) return NDBT_FAILED; if (restarter.waitClusterStarted()) return NDBT_FAILED; } return NDBT_OK; }
int runLateCommit(NDBT_Context* ctx, NDBT_Step* step){ int result = NDBT_OK; int loops = ctx->getNumLoops(); int records = ctx->getNumRecords(); NdbRestarter restarter; HugoOperations hugoOps(*ctx->getTab()); Ndb* pNdb = GETNDB(step); int i = 0; while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){ g_info << i << ": "; if(hugoOps.startTransaction(pNdb) != 0) return NDBT_FAILED; if(hugoOps.pkUpdateRecord(pNdb, 1, 128) != 0) return NDBT_FAILED; if(hugoOps.execute_NoCommit(pNdb) != 0) return NDBT_FAILED; Uint32 transNode= hugoOps.getTransaction()->getConnectedNodeId(); int id = i % restarter.getNumDbNodes(); int nodeId; while((nodeId = restarter.getDbNodeId(id)) == transNode) id = (id + 1) % restarter.getNumDbNodes(); ndbout << "Restart node " << nodeId << endl; restarter.restartOneDbNode(nodeId, /** initial */ false, /** nostart */ true, /** abort */ true); restarter.waitNodesNoStart(&nodeId, 1); int res; if(i & 1) res= hugoOps.execute_Commit(pNdb); else res= hugoOps.execute_Rollback(pNdb); ndbout_c("res= %d", res); hugoOps.closeTransaction(pNdb); restarter.startNodes(&nodeId, 1); restarter.waitNodesStarted(&nodeId, 1); if(i & 1) { if(res != 286) return NDBT_FAILED; } else { if(res != 0) return NDBT_FAILED; } i++; } return NDBT_OK; }
int NdbBackup::Fail(NdbRestarter& _restarter, int *Fail_codes, const int sz, bool onMaster){ CHECK(_restarter.waitClusterStarted() == 0, "waitClusterStarted failed"); int nNodes = _restarter.getNumDbNodes(); myRandom48Init(NdbTick_CurrentMillisecond()); for(int i = 0; i<sz; i++){ int error = Fail_codes[i]; unsigned int backupId; const int masterNodeId = _restarter.getMasterNodeId(); CHECK(masterNodeId > 0, "getMasterNodeId failed"); int nodeId; nodeId = masterNodeId; if (!onMaster) { int randomId; while (nodeId == masterNodeId) { randomId = myRandom48(nNodes); nodeId = _restarter.getDbNodeId(randomId); } } g_err << "NdbBackup::Fail node = " << nodeId << " error code = " << error << " masterNodeId = " << masterNodeId << endl; CHECK(_restarter.insertErrorInNode(nodeId, error) == 0, "failed to set error insert"); g_info << "error inserted" << endl; g_info << "waiting some before starting backup" << endl; g_info << "starting backup" << endl; int r = start(backupId); g_info << "r = " << r << " (which should fail) started with id = " << backupId << endl; if (r == 0) { g_err << "Backup should have failed on error_insertion " << error << endl << "Master = " << masterNodeId << "Node = " << nodeId << endl; return NDBT_FAILED; } CHECK(_restarter.waitClusterStarted() == 0, "waitClusterStarted failed"); CHECK(_restarter.insertErrorInNode(nodeId, 10099) == 0, "failed to set error insert"); NdbSleep_SecSleep(5); int val2[] = { 24, 2424 }; CHECK(_restarter.dumpStateAllNodes(val2, 2) == 0, "failed to check backup resources RestartOnErrorInsert"); } return NDBT_OK; }
int NdbBackup::NF(NdbRestarter& _restarter, int *NFDuringBackup_codes, const int sz, bool onMaster){ int nNodes = _restarter.getNumDbNodes(); { if(nNodes == 1) return NDBT_OK; int nodeId = _restarter.getMasterNodeId(); CHECK(_restarter.restartOneDbNode(nodeId, false, true, true) == 0, "Could not restart node "<< nodeId); CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0, "waitNodesNoStart failed"); CHECK(_restarter.startNodes(&nodeId, 1) == 0, "failed to start node"); } CHECK(_restarter.waitClusterStarted() == 0, "waitClusterStarted failed"); myRandom48Init(NdbTick_CurrentMillisecond()); for(int i = 0; i<sz; i++){ int error = NFDuringBackup_codes[i]; unsigned int backupId; const int masterNodeId = _restarter.getMasterNodeId(); CHECK(masterNodeId > 0, "getMasterNodeId failed"); int nodeId; nodeId = masterNodeId; if (!onMaster) { int randomId; while (nodeId == masterNodeId) { randomId = myRandom48(nNodes); nodeId = _restarter.getDbNodeId(randomId); } } g_err << "NdbBackup::NF node = " << nodeId << " error code = " << error << " masterNodeId = " << masterNodeId << endl; int val[] = { DumpStateOrd::CmvmiSetRestartOnErrorInsert, 1 }; CHECK(_restarter.dumpStateOneNode(nodeId, val, 2) == 0, "failed to set RestartOnErrorInsert"); CHECK(_restarter.insertErrorInNode(nodeId, error) == 0, "failed to set error insert"); g_info << "error inserted" << endl; NdbSleep_SecSleep(1); g_info << "starting backup" << endl; int r = start(backupId); g_info << "r = " << r << " (which should fail) started with id = " << backupId << endl; if (r == 0) { g_err << "Backup should have failed on error_insertion " << error << endl << "Master = " << masterNodeId << "Node = " << nodeId << endl; return NDBT_FAILED; } CHECK(_restarter.waitNodesNoStart(&nodeId, 1) == 0, "waitNodesNoStart failed"); g_info << "number of nodes running " << _restarter.getNumDbNodes() << endl; if (_restarter.getNumDbNodes() != nNodes) { g_err << "Failure: cluster not up" << endl; return NDBT_FAILED; } g_info << "starting new backup" << endl; CHECK(start(backupId) == 0, "failed to start backup"); g_info << "(which should succeed) started with id = " << backupId << endl; g_info << "starting node" << endl; CHECK(_restarter.startNodes(&nodeId, 1) == 0, "failed to start node"); CHECK(_restarter.waitClusterStarted() == 0, "waitClusterStarted failed"); g_info << "node started" << endl; int val2[] = { 24, 2424 }; CHECK(_restarter.dumpStateAllNodes(val2, 2) == 0, "failed to check backup resources RestartOnErrorInsert"); CHECK(_restarter.insertErrorInNode(nodeId, 10099) == 0, "failed to set error insert"); NdbSleep_SecSleep(1); } return NDBT_OK; }
int runRestarter(NDBT_Context* ctx, NDBT_Step* step) { int result = NDBT_OK; int loops = ctx->getNumLoops(); int sync_threads = ctx->getProperty("SyncThreads", (unsigned)0); int sleep0 = ctx->getProperty("Sleep0", (unsigned)0); int sleep1 = ctx->getProperty("Sleep1", (unsigned)0); int randnode = ctx->getProperty("RandNode", (unsigned)0); NdbRestarter restarter; int i = 0; int lastId = 0; if (restarter.getNumDbNodes() < 2) { ctx->stopTest(); return NDBT_OK; } if(restarter.waitClusterStarted() != 0) { g_err << "Cluster failed to start" << endl; return NDBT_FAILED; } if (loops > restarter.getNumDbNodes()) loops = restarter.getNumDbNodes(); while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()) { int id = lastId % restarter.getNumDbNodes(); if (randnode == 1) { id = rand() % restarter.getNumDbNodes(); } int nodeId = restarter.getDbNodeId(id); ndbout << "Restart node " << nodeId << endl; if(restarter.restartOneDbNode(nodeId, false, true, true) != 0) { g_err << "Failed to restartNextDbNode" << endl; result = NDBT_FAILED; break; } if (restarter.waitNodesNoStart(&nodeId, 1)) { g_err << "Failed to waitNodesNoStart" << endl; result = NDBT_FAILED; break; } if (sleep1) NdbSleep_MilliSleep(sleep1); if (restarter.startNodes(&nodeId, 1)) { g_err << "Failed to start node" << endl; result = NDBT_FAILED; break; } if(restarter.waitClusterStarted() != 0) { g_err << "Cluster failed to start" << endl; result = NDBT_FAILED; break; } if (sleep0) NdbSleep_MilliSleep(sleep0); ctx->sync_up_and_wait("PauseThreads", sync_threads); lastId++; i++; } ctx->stopTest(); return result; }