/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/
*/

/**
 * Return the path to the definitions file for the scalars.
 */
function getDefinitionsPath() {
  // Write the scalar definition to the spec file in the binary directory.
  let definitionFile = Cc["@mozilla.org/file/local;1"].createInstance(
    Ci.nsIFile
  );
  definitionFile = Services.dirsvc.get("GreD", Ci.nsIFile);
  definitionFile.append("ScalarArtifactDefinitions.json");
  return definitionFile.path;
}

add_task(async function test_setup() {
  do_get_profile();
});

add_task(
  {
    // The test needs to write a file, and that fails in tests on Android.
    // We don't really need the Android coverage, so skip on Android.
    skip_if: () => AppConstants.platform == "android",
  },
  async function test_invalidJSON() {
    const INVALID_JSON = "{ invalid,JSON { {1}";
    const FILE_PATH = getDefinitionsPath();

    // Write a corrupted JSON file.
    await IOUtils.writeUTF8(FILE_PATH, INVALID_JSON, {
      mode: "overwrite",
    });

    // Simulate Firefox startup. This should not throw!
    await TelemetryController.testSetup();
    await TelemetryController.testPromiseJsProbeRegistration();

    // Cleanup.
    await TelemetryController.testShutdown();
    await IOUtils.remove(FILE_PATH);
  }
);

add_task(
  {
    // The test needs to write a file, and that fails in tests on Android.
    // We don't really need the Android coverage, so skip on Android.
    skip_if: () => AppConstants.platform == "android",
  },
  async function test_dynamicBuiltin() {
    const DYNAMIC_SCALAR_SPEC = {
      "telemetry.test": {
        builtin_dynamic: {
          kind: "nsITelemetry::SCALAR_TYPE_COUNT",
          expires: "never",
          record_on_release: false,
          keyed: false,
        },
        builtin_dynamic_other: {
          kind: "nsITelemetry::SCALAR_TYPE_BOOLEAN",
          expires: "never",
          record_on_release: false,
          keyed: false,
        },
        builtin_dynamic_expired: {
          kind: "nsITelemetry::SCALAR_TYPE_BOOLEAN",
          expires: AppConstants.MOZ_APP_VERSION,
          record_on_release: false,
          keyed: false,
        },
        builtin_dynamic_multi: {
          kind: "nsITelemetry::SCALAR_TYPE_COUNT",
          expired: false,
          record_on_release: false,
          keyed: false,
          stores: ["main", "sync"],
        },
        builtin_dynamic_sync_only: {
          kind: "nsITelemetry::SCALAR_TYPE_COUNT",
          expired: false,
          record_on_release: false,
          keyed: false,
          stores: ["sync"],
        },
      },
    };

    Telemetry.clearScalars();

    // Let's write to the definition file to also cover the file
    // loading part.
    const FILE_PATH = getDefinitionsPath();
    await IOUtils.writeJSON(FILE_PATH, DYNAMIC_SCALAR_SPEC);

    // Start TelemetryController to trigger loading the specs.
    await TelemetryController.testReset();
    await TelemetryController.testPromiseJsProbeRegistration();

    // Store to that scalar.
    const TEST_SCALAR1 = "telemetry.test.builtin_dynamic";
    const TEST_SCALAR2 = "telemetry.test.builtin_dynamic_other";
    const TEST_SCALAR3 = "telemetry.test.builtin_dynamic_multi";
    const TEST_SCALAR4 = "telemetry.test.builtin_dynamic_sync_only";
    const TEST_SCALAR5 = "telemetry.test.builtin_dynamic_expired";
    Telemetry.scalarSet(TEST_SCALAR1, 3785);
    Telemetry.scalarSet(TEST_SCALAR2, true);
    Telemetry.scalarSet(TEST_SCALAR3, 1337);
    Telemetry.scalarSet(TEST_SCALAR4, 31337);
    Telemetry.scalarSet(TEST_SCALAR5, true);

    // Check the values we tried to store.
    const scalars = Telemetry.getSnapshotForScalars("main", false).parent;
    const syncScalars = Telemetry.getSnapshotForScalars("sync", false).parent;

    // Check that they are serialized to the correct format.
    Assert.equal(
      typeof scalars[TEST_SCALAR1],
      "number",
      TEST_SCALAR1 + " must be serialized to the correct format."
    );
    Assert.ok(
      Number.isInteger(scalars[TEST_SCALAR1]),
      TEST_SCALAR1 + " must be a finite integer."
    );
    Assert.equal(
      scalars[TEST_SCALAR1],
      3785,
      TEST_SCALAR1 + " must have the correct value."
    );
    Assert.equal(
      typeof scalars[TEST_SCALAR2],
      "boolean",
      TEST_SCALAR2 + " must be serialized to the correct format."
    );
    Assert.equal(
      scalars[TEST_SCALAR2],
      true,
      TEST_SCALAR2 + " must have the correct value."
    );

    Assert.equal(
      typeof scalars[TEST_SCALAR3],
      "number",
      `${TEST_SCALAR3} must be serialized to the correct format.`
    );
    Assert.equal(
      scalars[TEST_SCALAR3],
      1337,
      `${TEST_SCALAR3} must have the correct value.`
    );
    Assert.equal(
      typeof syncScalars[TEST_SCALAR3],
      "number",
      `${TEST_SCALAR3} must be serialized in the sync store to the correct format.`
    );
    Assert.equal(
      syncScalars[TEST_SCALAR3],
      1337,
      `${TEST_SCALAR3} must have the correct value in the sync snapshot.`
    );

    Assert.ok(
      !(TEST_SCALAR4 in scalars),
      `${TEST_SCALAR4} must not be in the main store.`
    );
    Assert.equal(
      typeof syncScalars[TEST_SCALAR4],
      "number",
      `${TEST_SCALAR4} must be in the sync snapshot.`
    );
    Assert.equal(
      syncScalars[TEST_SCALAR4],
      31337,
      `${TEST_SCALAR4} must have the correct value.`
    );

    // Clean up.
    await TelemetryController.testShutdown();
    await IOUtils.remove(FILE_PATH);
  }
);

add_task(async function test_keyedDynamicBuiltin() {
  Telemetry.clearScalars();

  // Register the built-in scalars (let's not take the I/O hit).
  Telemetry.registerBuiltinScalars("telemetry.test", {
    builtin_dynamic_keyed: {
      kind: Ci.nsITelemetry.SCALAR_TYPE_COUNT,
      expired: false,
      record_on_release: false,
      keyed: true,
    },
  });

  // Store to that scalar.
  const TEST_SCALAR1 = "telemetry.test.builtin_dynamic_keyed";
  Telemetry.keyedScalarSet(TEST_SCALAR1, "test-key", 3785);

  // Check the values we tried to store.
  const scalars = Telemetry.getSnapshotForKeyedScalars("main", false).parent;

  // Check that they are serialized to the correct format.
  Assert.equal(
    typeof scalars[TEST_SCALAR1],
    "object",
    TEST_SCALAR1 + " must be a keyed scalar."
  );
  Assert.equal(
    typeof scalars[TEST_SCALAR1]["test-key"],
    "number",
    TEST_SCALAR1 + " must be serialized to the correct format."
  );
  Assert.ok(
    Number.isInteger(scalars[TEST_SCALAR1]["test-key"]),
    TEST_SCALAR1 + " must be a finite integer."
  );
  Assert.equal(
    scalars[TEST_SCALAR1]["test-key"],
    3785,
    TEST_SCALAR1 + " must have the correct value."
  );
});
