<commit_before>/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
var { Toolbox } = require("devtools/client/framework/toolbox");
const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";

Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
registerCleanupFunction(() => {
  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
  delete window.resumeTest;
})

// Wait until an action of `type` is dispatched. This is different
// then `_afterDispatchDone` because it doesn't wait for async actions
// to be done/errored. Use this if you want to listen for the "start"
// action of an async operation (somewhat rare).
function waitForNextDispatch(store, type) {
  return new Promise(resolve => {
    store.dispatch({
      // Normally we would use `services.WAIT_UNTIL`, but use the
      // internal name here so tests aren't forced to always pass it
      // in
      type: "@@service/waitUntil",
      predicate: action => action.type === type,
      run: (dispatch, getState, action) => {
        resolve(action);
      }
    });
  });
}

// Wait until an action of `type` is dispatched. If it's part of an
// async operation, wait until the `status` field is "done" or "error"
function _afterDispatchDone(store, type) {
  return new Promise(resolve => {
    store.dispatch({
      // Normally we would use `services.WAIT_UNTIL`, but use the
      // internal name here so tests aren't forced to always pass it
      // in
      type: "@@service/waitUntil",
      predicate: action => {
        if (action.type === type) {
          return action.status ?
            (action.status === "done" || action.status === "error") :
            true;
        }
      },
      run: (dispatch, getState, action) => {
        resolve(action);
      }
    });
  });
}

function waitForDispatch(dbg, type, eventRepeat = 1) {
  let count = 0;

  return Task.spawn(function* () {
    info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)");
    while (count < eventRepeat) {
      yield _afterDispatchDone(dbg.store, type);
      count++;
      info(type + " dispatched " + count + " time(s)");
    }
  });
}

function waitForThreadEvents(dbg, eventName) {
  info("Waiting for thread event '" + eventName + "' to fire.");
  const thread = dbg.toolbox.threadClient;

  return new Promise(function(resolve, reject) {
    thread.addListener(eventName, function onEvent(eventName, ...args) {
      info("Thread event '" + eventName + "' fired.");
      thread.removeListener(eventName, onEvent);
      resolve.apply(resolve, args);
    });
  });
}

function waitForState(dbg, predicate) {
  return new Promise(resolve => {
    const unsubscribe = dbg.store.subscribe(() => {
      if (predicate(dbg.store.getState())) {
        unsubscribe();
        resolve();
      }
    });
  });
}

function waitForSources(dbg, ...sources) {
  if(sources.length === 0) {
    return Promise.resolve();
  }

  info("Waiting on sources: " + sources.join(", "));
  const {selectors: {getSources}, store} = dbg;
  return Promise.all(sources.map(url => {
    function sourceExists(state) {
      return getSources(state).some(s => s.get("url").includes(url));
    }

    if(!sourceExists(store.getState())) {
      return waitForState(dbg, sourceExists);
    }
  }));
}

function assertPausedLocation(dbg, source, line) {
  const { selectors: { getSelectedSource, getPause }, getState } = dbg;
  source = findSource(dbg, source);

  // Check the selected source
  is(getSelectedSource(getState()).get("url"), source.url);

  // Check the pause location
  const location = getPause(getState()).getIn(["frame", "location"]);
  is(location.get("sourceId"), source.id);
  is(location.get("line"), line);

  // Check the debug line
  ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
     "Line is highlighted as paused");
}

function assertHighlightLocation(dbg, source, line) {
  const { selectors: { getSelectedSource, getPause }, getState } = dbg;
  source = findSource(dbg, source);

  // Check the selected source
  is(getSelectedSource(getState()).get("url"), source.url);

  // Check the highlight line
  const lineEl = findElement(dbg, "highlightLine");
  ok(lineEl, "Line is highlighted");
  ok(isVisibleWithin(findElement(dbg, "codeMirror"), lineEl),
     "Highlighted line is visible");
  ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("highlight-line"),
     "Line is highlighted");
}

function isPaused(dbg) {
  const { selectors: { getPause }, getState } = dbg;
  return !!getPause(getState());
}

const waitForPaused = Task.async(function* (dbg) {
  // We want to make sure that we get both a real paused event and
  // that the state is fully populated. The client may do some more
  // work (call other client methods) before populating the state.
  return Promise.all([
    yield waitForThreadEvents(dbg, "paused"),
    yield waitForState(dbg, state => {
      const pause = dbg.selectors.getPause(state);
      // Make sure we have the paused state.
      if(!pause) {
        return false;
      }
      // Make sure the source text is completely loaded for the
      // source we are paused in.
      const sourceId = pause.getIn(["frame", "location", "sourceId"]);
      const sourceText = dbg.selectors.getSourceText(dbg.getState(), sourceId);
      return sourceText && !sourceText.get("loading");
    })
  ]);
});

const initDebugger = Task.async(function* (url, ...sources) {
  const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
  const win = toolbox.getPanel("jsdebugger").panelWin;
  const store = win.Debugger.store;
  const { getSources } = win.Debugger.selectors;

  const dbg = {
    actions: win.Debugger.actions,
    selectors: win.Debugger.selectors,
    getState: store.getState,
    store: store,
    client: win.Debugger.client,
    toolbox: toolbox,
    win: win
  };

  yield waitForSources(dbg, ...sources);
  return dbg;
});

window.resumeTest = undefined;
function pauseTest() {
  info("Test paused. Invoke resumeTest to continue.");
  return new Promise(resolve => resumeTest = resolve);
}

// Actions

function findSource(dbg, url) {
  if(typeof url !== "string") {
    // Support passing in a source object itelf all APIs that use this
    // function support both styles
    const source = url;
    return source;
  }

  const sources = dbg.selectors.getSources(dbg.getState());
  const source = sources.find(s => s.get("url").includes(url));
<commit_msg>Inline test Tasks<commit_after>/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
var { Toolbox } = require("devtools/client/framework/toolbox");
const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";

Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
registerCleanupFunction(() => {
  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
  delete window.resumeTest;
})

// Wait until an action of `type` is dispatched. This is different
// then `_afterDispatchDone` because it doesn't wait for async actions
// to be done/errored. Use this if you want to listen for the "start"
// action of an async operation (somewhat rare).
function waitForNextDispatch(store, type) {
  return new Promise(resolve => {
    store.dispatch({
      // Normally we would use `services.WAIT_UNTIL`, but use the
      // internal name here so tests aren't forced to always pass it
      // in
      type: "@@service/waitUntil",
      predicate: action => action.type === type,
      run: (dispatch, getState, action) => {
        resolve(action);
      }
    });
  });
}

// Wait until an action of `type` is dispatched. If it's part of an
// async operation, wait until the `status` field is "done" or "error"
function _afterDispatchDone(store, type) {
  return new Promise(resolve => {
    store.dispatch({
      // Normally we would use `services.WAIT_UNTIL`, but use the
      // internal name here so tests aren't forced to always pass it
      // in
      type: "@@service/waitUntil",
      predicate: action => {
        if (action.type === type) {
          return action.status ?
            (action.status === "done" || action.status === "error") :
            true;
        }
      },
      run: (dispatch, getState, action) => {
        resolve(action);
      }
    });
  });
}

function waitForDispatch(dbg, type, eventRepeat = 1) {
  let count = 0;

  return Task.spawn(function* () {
    info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)");
    while (count < eventRepeat) {
      yield _afterDispatchDone(dbg.store, type);
      count++;
      info(type + " dispatched " + count + " time(s)");
    }
  });
}

function waitForThreadEvents(dbg, eventName) {
  info("Waiting for thread event '" + eventName + "' to fire.");
  const thread = dbg.toolbox.threadClient;

  return new Promise(function(resolve, reject) {
    thread.addListener(eventName, function onEvent(eventName, ...args) {
      info("Thread event '" + eventName + "' fired.");
      thread.removeListener(eventName, onEvent);
      resolve.apply(resolve, args);
    });
  });
}

function waitForState(dbg, predicate) {
  return new Promise(resolve => {
    const unsubscribe = dbg.store.subscribe(() => {
      if (predicate(dbg.store.getState())) {
        unsubscribe();
        resolve();
      }
    });
  });
}

function waitForSources(dbg, ...sources) {
  if(sources.length === 0) {
    return Promise.resolve();
  }

  info("Waiting on sources: " + sources.join(", "));
  const {selectors: {getSources}, store} = dbg;
  return Promise.all(sources.map(url => {
    function sourceExists(state) {
      return getSources(state).some(s => s.get("url").includes(url));
    }

    if(!sourceExists(store.getState())) {
      return waitForState(dbg, sourceExists);
    }
  }));
}

function assertPausedLocation(dbg, source, line) {
  const { selectors: { getSelectedSource, getPause }, getState } = dbg;
  source = findSource(dbg, source);

  // Check the selected source
  is(getSelectedSource(getState()).get("url"), source.url);

  // Check the pause location
  const location = getPause(getState()).getIn(["frame", "location"]);
  is(location.get("sourceId"), source.id);
  is(location.get("line"), line);

  // Check the debug line
  ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
     "Line is highlighted as paused");
}

function assertHighlightLocation(dbg, source, line) {
  const { selectors: { getSelectedSource, getPause }, getState } = dbg;
  source = findSource(dbg, source);

  // Check the selected source
  is(getSelectedSource(getState()).get("url"), source.url);

  // Check the highlight line
  const lineEl = findElement(dbg, "highlightLine");
  ok(lineEl, "Line is highlighted");
  ok(isVisibleWithin(findElement(dbg, "codeMirror"), lineEl),
     "Highlighted line is visible");
  ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("highlight-line"),
     "Line is highlighted");
}

function isPaused(dbg) {
  const { selectors: { getPause }, getState } = dbg;
  return !!getPause(getState());
}

function waitForPaused(dbg) {
  return Task.spawn(function* () {
    // We want to make sure that we get both a real paused event and
    // that the state is fully populated. The client may do some more
    // work (call other client methods) before populating the state.
    yield waitForThreadEvents(dbg, "paused"),
    yield waitForState(dbg, state => {
      const pause = dbg.selectors.getPause(state);
      // Make sure we have the paused state.
      if(!pause) {
        return false;
      }
      // Make sure the source text is completely loaded for the
      // source we are paused in.
      const sourceId = pause.getIn(["frame", "location", "sourceId"]);
      const sourceText = dbg.selectors.getSourceText(dbg.getState(), sourceId);
      return sourceText && !sourceText.get("loading");
    });
  });
};

function initDebugger(url, ...sources) {
  return Task.spawn(function* () {
    const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
    const win = toolbox.getPanel("jsdebugger").panelWin;
    const store = win.Debugger.store;
    const { getSources } = win.Debugger.selectors;

    const dbg = {
      actions: win.Debugger.actions,
      selectors: win.Debugger.selectors,
      getState: store.getState,
      store: store,
      client: win.Debugger.client,
      toolbox: toolbox,
      win: win
    };

    yield waitForSources(dbg, ...sources);

    return dbg;
  });
};

window.resumeTest = undefined;
function pauseTest() {
  info("Test paused. Invoke resumeTest to continue.");
  return new Promise(resolve => resumeTest = resolve);
}

// Actions

function findSource(dbg, url) {
  if(typeof url !== "string") {
    // Support passing in a source object itelf all APIs that use this
    // function support both styles
    const source = url;
    return source;
  }

  const sources = dbg.selectors.getSources(dbg.getState());
  const source = sources.find(s => s.get("url").includes(url));

{'fraction_non_alphanumeric': 0.14254750379307854, 'fraction_numerical': 0.0017339787587602052, 'mean_word_length': 3.3310387984981227, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '224514', 'n_tokens_mistral': 4237, 'n_tokens_neox': 4164, 'n_words': 1122}