// Test multiple debuggers, GCs, and zones interacting with each other.
//
// Note: when observing both globals, but GC'ing in only one, we don't test that
// we *didn't* GC in the other zone because GCs are finicky and unreliable. That
// used to work when this was a jit-test, but in the process of migrating to
// xpcshell, we lost some amount of reliability and determinism.

const root1 = newGlobal();
const dbg1 = new Debugger();
dbg1.addDebuggee(root1)

const root2 = newGlobal();
const dbg2 = new Debugger();
dbg2.addDebuggee(root2)

let fired1 = false;
let fired2 = false;
dbg1.memory.onGarbageCollection = _ => fired1 = true;
dbg2.memory.onGarbageCollection = _ => fired2 = true;

Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
registerCleanupFunction(() => {
  Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
});

function reset() {
  fired1 = false;
  fired2 = false;
}

function run_test() {
  do_test_pending();

  gc();
  executeSoon(() => {
    reset();

    // GC 1 only
    root1.eval(`gc(this)`);
    executeSoon(() => {
      equal(fired1, true);

      // GC 2 only
      reset();
      root2.eval(`gc(this)`);
      executeSoon(() => {
        equal(fired2, true);

        // Full GC
        reset();
        gc();
        executeSoon(() => {
          equal(fired1, true);
          equal(fired2, true);

          // Full GC with no debuggees
          reset();
          dbg1.removeAllDebuggees();
          dbg2.removeAllDebuggees();
          gc();
          executeSoon(() => {
            equal(fired1, false);
            equal(fired2, false);

            // One debugger with multiple debuggees in different zones.

            dbg1.addDebuggee(root1);
            dbg1.addDebuggee(root2);

            // Just debuggee 1
            reset();
            root1.eval(`gc(this)`);
            executeSoon(() => {
              equal(fired1, true);
              equal(fired2, false);

              // Just debuggee 2
              reset();
              root2.eval(`gc(this)`);
              executeSoon(() => {
                equal(fired1, true);
                equal(fired2, false);

                // All debuggees
                reset();
                gc();
                executeSoon(() => {
                  equal(fired1, true);
                  equal(fired2, false);
                  do_test_finished();
                });
              });
            });
          });
        });
      });
    });
  });
}
