/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Invoke `profile_is_slim` background task, have it *not* remove its
// temporary profile, and verify that the temporary profile doesn't
// contain unexpected files and/or directories.
//
// N.b.: the background task invocation is a production Firefox
// running in automation; that means it can only connect to localhost
// (and it is not configured to connect to mochi.test, etc).
// Therefore we run our own server for it to connect to.

const { HttpServer } = ChromeUtils.importESModule(
  "resource://testing-common/httpd.sys.mjs"
);
let server;

setupProfileService();

let successCount = 0;
function success(metadata, response) {
  response.setStatusLine(metadata.httpVersion, 200, "OK");
  let str = "success";
  response.write(str, str.length);
  successCount += 1;
}

add_setup(() => {
  server = new HttpServer();
  server.registerPathHandler("/success", success);
  server.start(-1);

  registerCleanupFunction(async () => {
    await server.stop();
  });
});

add_task(async function test_backgroundtask_profile_is_slim() {
  let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].getService(
    Ci.nsIToolkitProfileService
  );

  let profD = do_get_profile();
  profD.append("test_profile_is_slim");
  let profile = profileService.createUniqueProfile(
    profD,
    "test_profile_is_slim",
    "tests"
  );

  registerCleanupFunction(() => {
    profile.remove(true);
  });

  let exitCode = await do_backgroundtask("profile_is_slim", {
    extraArgs: [`http://localhost:${server.identity.primaryPort}/success`],
    extraEnv: {
      XRE_PROFILE_PATH: profile.rootDir.path,
      MOZ_BACKGROUNDTASKS_NO_REMOVE_PROFILE: "1",
    },
  });

  Assert.equal(
    0,
    exitCode,
    "The fetch background task exited with exit code 0"
  );
  Assert.equal(
    1,
    successCount,
    "The fetch background task reached the test server 1 time"
  );

  assertProfileIsSlim(profile.rootDir);
});

const expected = [
  { relPath: "lock", condition: AppConstants.platform == "linux" },
  {
    relPath: ".parentlock",
    condition:
      AppConstants.platform == "linux" || AppConstants.platform == "macosx",
  },
  {
    relPath: "parent.lock",
    condition:
      AppConstants.platform == "win" || AppConstants.platform == "macosx",
  },
  // TODO: verify that directory is empty.
  { relPath: "cache2", isDirectory: true },
  { relPath: "compatibility.ini" },
  { relPath: "crashes", isDirectory: true },
  { relPath: "data", isDirectory: true },
  { relPath: "local", isDirectory: true },
  { relPath: "minidumps", isDirectory: true },
  // `mozinfo.json` is an artifact of the testing infrastructure.
  { relPath: "mozinfo.json" },
  { relPath: "pkcs11.txt" },
  { relPath: "prefs.js" },
  { relPath: "security_state", isDirectory: true },
  // TODO: verify that directory is empty.
  { relPath: "startupCache", isDirectory: true },
  { relPath: "times.json" },
];

async function assertProfileIsSlim(profile) {
  Assert.ok(profile.exists(), `Profile directory does exist: ${profile.path}`);

  // We collect unexpected results, log them all, and then assert
  // there are none to provide the most feedback per iteration.
  let unexpected = [];

  let typeLabel = { true: "directory", false: "file" };

  for (let entry of profile.directoryEntries) {
    // `getRelativePath` always uses '/' as path separator.
    let relPath = entry.getRelativePath(profile);

    info(`Witnessed directory entry '${relPath}'.`);

    let expectation = expected.find(
      it => it.relPath == relPath && (!("condition" in it) || it.condition)
    );
    if (!expectation) {
      info(`UNEXPECTED: Directory entry '${relPath}' is NOT expected!`);
      unexpected.push(relPath);
    } else if (
      typeLabel[!!expectation.isDirectory] != typeLabel[entry.isDirectory()]
    ) {
      info(`UNEXPECTED: Directory entry '${relPath}' is NOT expected type!`);
      unexpected.push(relPath);
    }
  }

  Assert.deepEqual([], unexpected, "Expected nothing to report");
}
