lamchau
9/13/2015 - 3:49 AM

test dependencies: lodash and qunit (not by choice)

test dependencies: lodash and qunit (not by choice)

/*
 * This file is a ported version of
 * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/jtreg/util/concurrent/TimeUnit/Basic.java?view=co
 * which contained the following header:
 */

/*
 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/* @test
 * @bug 5057341 6363898
 * @summary Basic tests for TimeUnit
 * @author Martin Buchholz
 */

import TimeUnit from "../../../time-unit";
import {
  module,
  test
} from "qunit";

module("unit:time-unit (GNU compliance)");

test("TimeUnit (Java Source)", assert => {
  const values = Object.keys(TimeUnit)
    .sort((a, b) => TimeUnit[a].ordinal - TimeUnit[b].ordinal)
    .map(key => TimeUnit[key]);

  values.forEach(u => {
    assert.strictEqual(42, u.convert(42, u));
    values.forEach(v => {
      if (u.convert(42, v) >= 42) {
        assert.strictEqual(42, v.convert(u.convert(42, v), u));
      }
    });
  });

  assert.strictEqual(24, TimeUnit.HOURS.convert(1, TimeUnit.DAYS));
  assert.strictEqual(60, TimeUnit.MINUTES.convert(1, TimeUnit.HOURS));
  assert.strictEqual(60, TimeUnit.SECONDS.convert(1, TimeUnit.MINUTES));
  assert.strictEqual(1000, TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS));
  assert.strictEqual(1000, TimeUnit.MICROSECONDS.convert(1, TimeUnit.MILLISECONDS));
  assert.strictEqual(1000, TimeUnit.NANOSECONDS.convert(1, TimeUnit.MICROSECONDS));

  assert.strictEqual(24, TimeUnit.DAYS.toHours(1));
  assert.strictEqual(60, TimeUnit.HOURS.toMinutes(1));
  assert.strictEqual(60, TimeUnit.MINUTES.toSeconds(1));
  assert.strictEqual(1000, TimeUnit.SECONDS.toMillis(1));
  assert.strictEqual(1000, TimeUnit.MILLISECONDS.toMicros(1));
  assert.strictEqual(1000, TimeUnit.MICROSECONDS.toNanos(1));
});
import TimeUnit from "../../../time-unit";
import {
  gcd,
  AVAILABLE_TIME_UNITS
} from "../../../time-unit";

import {
  module,
  test
} from "qunit";

module("unit:time-unit");

test("TimeUnit", assert => {
  assert.ok(_.isObject(TimeUnit), "should TimeUnit should be the default export and exported as object");

  const values = Object.keys(TimeUnit).sort((a, b) => TimeUnit[a].ordinal - TimeUnit[b].ordinal),
    expectedProperties = [
      "name",
      "ordinal"
    ].sort(),
    expectedMethods = [
      "toNanos",
      "toMicros",
      "toMillis",
      "toSeconds",
      "toMinutes",
      "toHours",
      "toDays",
      "convert"
    ].sort();

  _.reduce(values, (current, next) => {
    const currentTime = TimeUnit[current],
      nextTime = TimeUnit[next],
      methods = _.functions(currentTime).sort(),
      properties = _.keys(_.pick(currentTime, _.negate(_.isFunction))).sort();

    // test each 'enum' while we're at it
    assert.deepEqual(properties, expectedProperties, `${current}: should have the same properties`);
    assert.deepEqual(methods, expectedMethods, `${current}: should have conversion method names`);

    assert.ok(currentTime.ordinal < nextTime.ordinal, `${current}: should be less than ${next}`);
    return next;
  });
});

test("gcd", assert => {
  function testGCD(value, unit, expectedValue, expectedUnit) {
    const factor = gcd(value, unit),
      actual = factor.convert(value, unit),
      message = `should calculate ${value} ${unit.name} for ${expectedValue} ${expectedUnit.name}`;

    assert.ok(actual === expectedValue && factor === expectedUnit, message.toLowerCase());
  }

  assert.ok(_.isFunction(gcd), "should be exported as a function");

  testGCD(72, TimeUnit.HOURS, 3, TimeUnit.DAYS);
  testGCD(73, TimeUnit.HOURS, 73, TimeUnit.HOURS);
  testGCD(60, TimeUnit.MINUTES, 1, TimeUnit.HOURS);
  testGCD(86400, TimeUnit.SECONDS, 1, TimeUnit.DAYS);
  testGCD(86400, TimeUnit.MINUTES, 60, TimeUnit.DAYS);
  testGCD(86401, TimeUnit.MINUTES, 86401, TimeUnit.MINUTES);
});

test("AVAILABLE_TIME_UNITS", assert => {
  assert.ok(_.isArray(AVAILABLE_TIME_UNITS), "should exported as an array");

  const values = Object.keys(TimeUnit).sort((a, b) => TimeUnit[a].ordinal - TimeUnit[b].ordinal);
  assert.deepEqual(values, AVAILABLE_TIME_UNITS, "should be sorted in ascending order");
});
/*
 * This file is a ported version of
 * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/TimeUnit.java
 * which contained the following notice:
 *
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

const MIN = -9007199254740992;
const MAX = 9007199254740992;
const C0 = 1;
const C1 = C0 * 1000;
const C2 = C1 * 1000;
const C3 = C2 * 1000;
const C4 = C3 * 60;
const C5 = C4 * 60;
const C6 = C5 * 24;

function x(d, m, over) {
  if (d > over) {
    return MAX;
  }
  if (d < -over) {
    return MIN;
  }
  return d * m;
}

/**
 * <code>
 *   TimeUnit.DAYS.toHours(1); // 24
 *   TimeUnit.HOURS.convert(1, TimeUnit.DAYS); // 24
 * </code>
 */
const TimeUnit = {
  // emulate Java enum
  NANOSECONDS: {
    get name()        { return "NANOSECONDS"; },
    get ordinal()     { return 0; },
    toNanos(d)        { return d; },
    toMicros(d)       { return d/(C1/C0); },
    toMillis(d)       { return d/(C2/C0); },
    toSeconds(d)      { return d/(C3/C0); },
    toMinutes(d)      { return d/(C4/C0); },
    toHours(d)        { return d/(C5/C0); },
    toDays(d)         { return d/(C6/C0); },
    convert(d, unit)  { return unit.toNanos(d); }
  },

  MICROSECONDS: {
    get name()        { return "MICROSECONDS"; },
    get ordinal()     { return 1; },
    toNanos(d)        { return x(d, C1/C0, MAX/(C1/C0)); },
    toMicros(d)       { return d; },
    toMillis(d)       { return d/(C2/C1); },
    toSeconds(d)      { return d/(C3/C1); },
    toMinutes(d)      { return d/(C4/C1); },
    toHours(d)        { return d/(C5/C1); },
    toDays(d)         { return d/(C6/C1); },
    convert(d, unit)  { return unit.toMicros(d); }
  },

  MILLISECONDS: {
    get name()        { return "MILLISECONDS"; },
    get ordinal()     { return 2; },
    toNanos(d)        { return x(d, C2/C0, MAX/(C2/C0)); },
    toMicros(d)       { return x(d, C2/C1, MAX/(C2/C1)); },
    toMillis(d)       { return d; },
    toSeconds(d)      { return d/(C3/C2); },
    toMinutes(d)      { return d/(C4/C2); },
    toHours(d)        { return d/(C5/C2); },
    toDays(d)         { return d/(C6/C2); },
    convert(d, unit)  { return unit.toMillis(d); }
  },

  SECONDS: {
    get name()        { return "SECONDS"; },
    get ordinal()     { return 3; },
    toNanos(d)        { return x(d, C3/C0, MAX/(C3/C0)); },
    toMicros(d)       { return x(d, C3/C1, MAX/(C3/C1)); },
    toMillis(d)       { return x(d, C3/C2, MAX/(C3/C2)); },
    toSeconds(d)      { return d; },
    toMinutes(d)      { return d/(C4/C3); },
    toHours(d)        { return d/(C5/C3); },
    toDays(d)         { return d/(C6/C3); },
    convert(d, unit)  { return unit.toSeconds(d); }
  },

  MINUTES: {
    get name()        { return "MINUTES"; },
    get ordinal()     { return 4; },
    toNanos(d)        { return x(d, C4/C0, MAX/(C4/C0)); },
    toMicros(d)       { return x(d, C4/C1, MAX/(C4/C1)); },
    toMillis(d)       { return x(d, C4/C2, MAX/(C4/C2)); },
    toSeconds(d)      { return x(d, C4/C3, MAX/(C4/C3)); },
    toMinutes(d)      { return d; },
    toHours(d)        { return d/(C5/C4); },
    toDays(d)         { return d/(C6/C4); },
    convert(d, unit)  { return unit.toMinutes(d); }
  },

  HOURS: {
    get name()        { return "HOURS"; },
    get ordinal()     { return 5; },
    toNanos(d)        { return x(d, C5/C0, MAX/(C5/C0)); },
    toMicros(d)       { return x(d, C5/C1, MAX/(C5/C1)); },
    toMillis(d)       { return x(d, C5/C2, MAX/(C5/C2)); },
    toSeconds(d)      { return x(d, C5/C3, MAX/(C5/C3)); },
    toMinutes(d)      { return x(d, C5/C4, MAX/(C5/C4)); },
    toHours(d)        { return d; },
    toDays(d)         { return d/(C6/C5); },
    convert(d, unit)  { return unit.toHours(d); }
  },

  DAYS: {
    get name()        { return "DAYS"; },
    get ordinal()     { return 6; },
    toNanos(d)        { return x(d, C6/C0, MAX/(C6/C0)); },
    toMicros(d)       { return x(d, C6/C1, MAX/(C6/C1)); },
    toMillis(d)       { return x(d, C6/C2, MAX/(C6/C2)); },
    toSeconds(d)      { return x(d, C6/C3, MAX/(C6/C3)); },
    toMinutes(d)      { return x(d, C6/C4, MAX/(C6/C4)); },
    toHours(d)        { return x(d, C6/C5, MAX/(C6/C5)); },
    toDays(d)         { return d; },
    convert(d, unit)  { return unit.toDays(d); }
  }
};

export const AVAILABLE_TIME_UNITS = Object.keys(TimeUnit)
  .sort((a, b) => TimeUnit[a].ordinal - TimeUnit[b].ordinal);

const DESCENDING_TIME_UNITS = AVAILABLE_TIME_UNITS.slice().reverse();

// convert as "hours" "HOURS" "TimeUnit.HOURS" => TimeUnit.HOURS
export function asTimeUnit(unit) {
  if (_.isString(unit)) {
    unit = unit.toUpperCase();
  }

  let timeunit = _.find(TimeUnit, u => u === unit || u.name === unit);
  if (!timeunit) {
    Ember.assert(`Invalid '${unit}'. Available: ${AVAILABLE_TIME_UNITS.join(", ")}`);
  }
  return timeunit;
}

/**
 * <code>
 *   gcd(72, TimeUnit.HOURS); // TimeUnit.DAY
 *   gcd(73, TimeUnit.HOURS); // TimeUnit.HOURS
 * </code>
 */
export function gcd(value, unit) {
  Ember.assert("Value cannot be negative", value >= 0);
  Ember.assert(`Time unit must be one of the following ${AVAILABLE_TIME_UNITS.join(", ")}`,
     _.contains(AVAILABLE_TIME_UNITS, _.has(unit, "name") ? unit.name : null));

  let nanoseconds = TimeUnit.NANOSECONDS.convert(value, unit),
    result;
  
  // babel v5.1.5
  result = _.find(DESCENDING_TIME_UNITS, unit => {
      let timeunit = asTimeUnit(unit),
        conversion = timeunit.convert(nanoseconds, TimeUnit.NANOSECONDS);

      return conversion < MAX && Math.floor(conversion) === conversion;
    });
  return result ? asTimeUnit(result) : unit;

  // babel v5.8.24
  // for (let unit of timeunits) {
  //   let timeunit = asTimeUnit(result),
  //     conversion = timeunit.convert(nanoseconds, TimeUnit.NANOSECONDS);
  //
  //   if (conversion < MAX && Math.floor(conversion) === conversion) {
  //     return timeunit;
  //   }
  // }
  // return base;
}

export default TimeUnit;