/**
 * Basic test of killop functionality.
 *
 * Theory of operation: Creates two operations that will take a long time, sends killop for those
 * operations, and then attempts to infer that the operations died because of killop, and not for
 * some other reason.
 *
 * NOTES:
 * The long operations are count({$where: function () { while (1) ; } }).  These operations do not
 * terminate until the server determines that they've spent too much time in JS execution, typically
 * after 30 seconds of wall clock time have passed.  For these operations to take a long time, the
 * counted collection must not be empty; hence an initial write to the collection is required.
 */

t = db.jstests_killop;
t.drop();

t.save({x: 1});

/**
 * This function filters for the operations that we're looking for, based on their state and
 * the contents of their query object.
 */
function ops() {
    p = db.currentOp().inprog;
    ids = [];
    for (var i in p) {
        var o = p[i];
        // We *can't* check for ns, b/c it's not guaranteed to be there unless the query is active,
        // which
        // it may not be in our polling cycle - particularly b/c we sleep every second in both the
        // query and
        // the assert
        if ((o.active || o.waitingForLock) && o.query && o.query.query && o.query.query.$where &&
            o.query.count == "jstests_killop") {
            ids.push(o.opid);
        }
    }
    return ids;
}

jsTestLog("Starting long-running $where operation");
var s1 =
    startParallelShell("db.jstests_killop.count( { $where: function() { while( 1 ) { ; } } } )");
var s2 =
    startParallelShell("db.jstests_killop.count( { $where: function() { while( 1 ) { ; } } } )");

jsTestLog("Finding ops in currentOp() output");
o = [];
assert.soon(
    function() {
        o = ops();
        return o.length == 2;
    },
    {
      toString: function() {
          return tojson(db.currentOp().inprog);
      }
    },
    10000);
start = new Date();
jsTestLog("Killing ops");
db.killOp(o[0]);
db.killOp(o[1]);

jsTestLog("Waiting for ops to terminate");
[s1, s2].forEach(function(awaitShell) {
    var exitCode = awaitShell({checkExitSuccess: false});
    assert.neq(
        0, exitCode, "expected shell to exit abnormally due to JS execution being terminated");
});

// don't want to pass if timeout killed the js function.
var end = new Date();
var diff = end - start;
assert.lt(diff, 30000, "Start: " + start + "; end: " + end + "; diff: " + diff);