af-core 0.1.8

A core library and async runtime for Rust applications.
Documentation
// Copyright © 2021 Alexandra Frydl
//
// 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/.

use af_core::task;
use af_core::test::prelude::*;

/// Test the `time` module.
pub fn test(cx: &mut test::Context) {
  cx.scope("::Date", test_date);
  cx.scope("::Duration", test_duration);
  cx.scope("::Span", test_span);
  cx.scope("::Time", test_time);
  cx.scope("::Zone", test_zone);
}

/// Tests `Date`.
fn test_date(cx: &mut test::Context) {
  test!(cx, "::next()", {
    let date = Date::from_ymd(2021, 1, 24);

    let next = date.next();
    let (year, month, day) = next.ymd();

    fail::when!(year != 2021);
    fail::when!(month != 1);
    fail::when!(day != 25);
  });

  test!(cx, "::prev()", {
    let date = Date::from_ymd(2021, 2, 1);

    let next = date.prev();
    let (year, month, day) = next.ymd();

    fail::when!(year != 2021);
    fail::when!(month != 1);
    fail::when!(day != 31);
  });

  test!(cx, "::to_time()", {
    let initial = Date::from_ymd(2021, 1, 24);

    let time = initial.to_time(time::UTC);
    let date = time.date();
    let (hour, minute, second) = time.hms();

    fail::when!(date != initial);
    fail::when!(hour != 0);
    fail::when!(minute != 0);
    fail::when!(second != 0);
  });
}

/// Tests `Duration`.
fn test_duration(cx: &mut test::Context) {
  test!(cx, "cannot be negative", {
    let secs = Duration::secs(-1.0).as_secs();

    fail::when!(secs != 0.0);
  });

  test!(cx, "::as_ms()", repeat = 100, {
    let expected = random::range(500..1_500) as f64;
    let actual = Duration::secs(expected / 1_000.0).as_ms().round_to_places(2);

    fail::when!(actual != expected);
  });

  test!(cx, "::as_hz()", repeat = 100, {
    let expected = random::range(1..10_000) as f64 / 10.0;
    let actual = Duration::secs(1.0 / expected).as_hz().round_to_places(2);

    fail::when!(actual != expected);
  });

  test!(cx, "::as_secs()", repeat = 100, {
    let expected = random::range(500_000..1_500_000) as f64;
    let actual = Duration::secs(expected).as_secs().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::as_mins()", repeat = 100, {
    let expected = random::range(500..1500) as f64;
    let actual = Duration::secs(expected * 60.0).as_mins().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::as_hours()", repeat = 100, {
    let expected = random::range(500..1500) as f64;
    let actual = Duration::secs(expected * 60.0 * 60.0).as_hours().round_to_places(1);

    fail::when!(actual != expected)
  });

  test!(cx, "::as_days()", repeat = 100, {
    let expected = random::range(500..1500) as f64;
    let actual = Duration::secs(expected * 60.0 * 60.0 * 24.0).as_days().round_to_places(1);

    fail::when!(actual != expected)
  });

  test!(cx, "::as_weeks()", repeat = 100, {
    let expected = random::range(500..1500) as f64;
    let actual = Duration::secs(expected * 7.0 * 24.0 * 60.0 * 60.0).as_weeks().round_to_places(1);

    fail::when!(actual != expected)
  });

  test!(cx, "::ms()", repeat = 100, {
    let expected = random::range(500..1500) as f64;
    let actual = Duration::ms(expected * 1_000.0).as_secs().round_to_places(2);

    fail::when!(actual != expected);
  });

  test!(cx, "::hz()", repeat = 100, {
    let expected = random::range(1..100) as f64 / 10.0;
    let actual = Duration::hz(1.0 / expected).as_secs().round_to_places(2);

    fail::when!(actual != expected);
  });

  test!(cx, "::secs()", repeat = 100, {
    let expected = random::range(50_000_000..150_000_000) as f64;
    let actual = Duration::secs(expected).as_secs().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::mins()", repeat = 100, {
    let expected = random::range(50_000_000..150_000_000) as f64;
    let actual = Duration::mins(expected / 60.0).as_secs().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::hours()", repeat = 100, {
    let expected = random::range(50_000_000..150_000_000) as f64;
    let actual = Duration::hours(expected / 60.0 / 60.0).as_secs().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::days()", repeat = 100, {
    let expected = random::range(50_000_000..150_000_000) as f64;
    let actual = Duration::days(expected / 60.0 / 60.0 / 24.0).as_secs().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::weeks()", repeat = 100, {
    let expected = random::range(50_000_000..150_000_000) as f64;
    let actual = Duration::weeks(expected / 60.0 / 60.0 / 24.0 / 7.0).as_secs().round_to_places(1);

    fail::when!(actual != expected);
  });

  test!(cx, "::is_finite()", {
    let infinite = Duration::secs(f64::INFINITY);
    let finite = Duration::secs(u32::MAX);

    fail::when!(infinite.is_finite());
    fail::when!(!finite.is_finite());
  });

  test!(cx, "::is_infinite()", {
    let infinite = Duration::secs(f64::INFINITY);
    let finite = Duration::secs(u32::MAX);

    fail::when!(!infinite.is_infinite());
    fail::when!(finite.is_infinite());
  });

  test!(cx, "::to_std()", {
    let ms = random::<u16>() as u128;
    let duration = Duration::ms(ms);

    let std_ms = duration.to_std().as_millis();

    fail::when!(std_ms != ms);
  });

  test!(cx, " + Self", {
    let a_secs = random::range(0.1..100.0);
    let b_secs = random::range(0.1..50.0);

    let sum = Duration::secs(a_secs) + Duration::secs(b_secs);

    fail::when!(sum.as_secs() != a_secs + b_secs);
  });

  test!(cx, " - Self", {
    let a_secs = random::range(50.0..100.0);
    let b_secs = random::range(0.1..50.0);

    let diff = Duration::secs(a_secs) - Duration::secs(b_secs);

    fail::when!(diff.as_secs() != a_secs - b_secs);
  });

  test!(cx, " - Self cannot be negative", {
    let diff = Duration::secs(1.0) - Duration::secs(2.0);

    fail::when!(diff != Duration::ZERO)
  });

  test!(cx, " * f64", {
    let secs = random::range(0.1..100.0);
    let multi = random::range(0.1..10.0);

    let product = Duration::secs(secs) * multi;

    fail::when!(product.as_secs() != secs * multi);
  });

  test!(cx, " * f64 cannot be negative", {
    let neg = Duration::secs(1) * -2.0;

    fail::when!(neg != Duration::ZERO);
  });

  test!(cx, " / f64", {
    let secs = random::range(0.1..100.0);
    let div = random::range(0.1..10.0);

    let quotient = Duration::secs(secs) / div;

    fail::when!(quotient.as_secs() != secs / div);
  });

  test!(cx, " / f64 cannot be negative", {
    let neg = Duration::secs(1) / -2.0;

    fail::when!(neg != Duration::ZERO);
  });
}

/// Tests `Span`.
fn test_span(cx: &mut test::Context) {
  use time::Span;

  test!(cx, "::new()", {
    let start = Time::now();
    let end = start + Duration::secs(random::range(0..100_000));

    let span = Span::new(start, end);

    fail::when!(span.start() != start);
    fail::when!(span.end() != end);
  });

  test!(cx, "::new() always orders start and end time correctly", {
    let earlier = Time::now();
    let later = earlier + Duration::secs(random::range(0..100_000));

    let normal = Span::new(earlier, later);
    let flipped = Span::new(later, earlier);

    fail::when!(normal != flipped);
  });

  test!(cx, "::contains()", {
    let start = Time::now();
    let middle = start + Duration::secs(random::range(1..50_000));
    let end = start + Duration::secs(random::range(50_000..100_000));
    let span = Span::new(start, end);

    fail::when!(span.contains(start - Duration::ms(0.001)));
    fail::when!(!span.contains(start));
    fail::when!(!span.contains(middle));
    fail::when!(!span.contains(end));
    fail::when!(span.contains(end + Duration::ms(0.001)));
  });

  test!(cx, "::duration()", repeat = 100, {
    let start = Time::now();
    let end = start + Duration::secs(random::range(50_000..100_000));
    let span = Span::new(start, end);

    let expected = end - start;
    let actual = span.duration();

    fail::when!(actual != expected);
  });

  test!(cx, "::overlaps()", {
    let start = Time::now();
    let end = start + Duration::secs(random::range(50_000..100_000));
    let span = Span::new(start, end);

    fail::when!(!span.overlaps(span));

    let one_s = Duration::secs(1.0);
    let five_s = Duration::secs(5.0);

    let before = Span::new(start - five_s, start - one_s);
    let before_eq = Span::new(start - one_s, start);
    let during_eq_start = Span::new(start, start + one_s);
    let during = Span::new(start + one_s, end + one_s);
    let during_eq_end = Span::new(start + one_s, end);
    let after_eq = Span::new(end, end + one_s);
    let after = Span::new(end + one_s, end + five_s);

    fail::when!(span.overlaps(before));
    fail::when!(!span.overlaps(before_eq));
    fail::when!(!span.overlaps(during_eq_start));
    fail::when!(!span.overlaps(during));
    fail::when!(!span.overlaps(during_eq_end));
    fail::when!(!span.overlaps(after_eq));
    fail::when!(span.overlaps(after));
  });

  test!(cx, " + Time", {
    let start = Time::now();
    let end = start + Duration::secs(1.0);

    let before = start - Duration::secs(1.0);
    let during = start + Duration::secs(0.5);
    let after = end + Duration::secs(1.0);

    let mut span = Span::new(start, end);

    span += before;

    fail::when!(span.start() != before);
    fail::when!(span.end() != end);

    span += during;

    fail::when!(span.start() != before);
    fail::when!(span.end() != end);

    span += after;

    fail::when!(span.start() != before);
    fail::when!(span.end() != after);
  });

  test!(cx, " + Self", {
    let start = Time::now();
    let end = start + Duration::secs(1.0);

    let before_3 = start - Duration::secs(3.0);
    let before_2 = start - Duration::secs(2.0);
    let before_1 = start - Duration::secs(1.0);
    let during_1 = start + Duration::secs(0.1);
    let during_9 = end - Duration::secs(0.9);
    let after_1 = end + Duration::secs(1.0);
    let after_2 = end + Duration::secs(2.0);
    let after_3 = end + Duration::secs(3.0);

    let mut span = Span::new(start, end);

    span += Span::new(before_1, start);

    fail::when!(span.start() != before_1);
    fail::when!(span.end() != end);

    span += Span::new(before_3, before_2);

    fail::when!(span.start() != before_3);
    fail::when!(span.end() != end);

    span += Span::new(start, during_1);
    span += Span::new(during_1, during_9);
    span += Span::new(during_9, end);

    fail::when!(span.start() != before_3);
    fail::when!(span.end() != end);

    span += Span::new(end, after_1);

    fail::when!(span.start() != before_3);
    fail::when!(span.end() != after_1);

    span += Span::new(after_2, after_3);

    fail::when!(span.start() != before_3);
    fail::when!(span.end() != after_3);
  });
}

/// Tests `Time`.
fn test_time(cx: &mut test::Context) {
  test!(cx, "::as_rfc3339", {
    let time = Time::from_unix_ms(1611510414260).to_utc();
    let output = time.as_rfc3339().to_string();

    fail::when!(output != "2021-01-24T17:46:54.260Z");
  });

  test!(cx, "::from_unix_ms()", {
    let time = Time::from_unix_ms(1611510414000).to_utc();
    let (year, month, day) = time.date().ymd();
    let (hour, minute, second) = time.hms();

    fail::when!(year != 2021);
    fail::when!(month != 1);
    fail::when!(day != 24);
    fail::when!(hour != 17);
    fail::when!(minute != 46);
    fail::when!(second != 54);
  });

  test!(cx, "::elapsed()", timeout = "100 ms", {
    let time = Time::now();

    task::sleep(Duration::ms(10)).await;

    let elapsed_ms = time.elapsed().as_ms();

    fail::when!(elapsed_ms < 9.0);
  });

  test!(cx, "::start_of_day()", {
    let time = Time::from_unix_ms(1611510414000).to_utc();

    let day = time.start_of_day();
    let (hour, minute, second) = day.hms();

    fail::when!(time.date() != day.date(), "Different days.");
    fail::when!(hour > 0);
    fail::when!(minute > 0);
    fail::when!(second > 0);
  });

  test!(cx, "::start_of_hour()", {
    let time = Time::from_unix_ms(1611510414260).to_utc();

    let day = time.start_of_hour();
    let (hour, minute, second) = day.hms();

    fail::when!(time.date() != day.date(), "Different days.");
    fail::when!(hour != 17);
    fail::when!(minute > 0);
    fail::when!(second > 0);
  });

  test!(cx, " + Duration", {
    let before = Time::from_unix_ms(1611510414000).to_utc();

    let after = before + Duration::mins(1.25);
    let (hour, minute, second) = after.hms();

    fail::when!(before.date() != after.date(), "Different days.");
    fail::when!(hour != 17);
    fail::when!(minute != 48);
    fail::when!(second != 9);
  });

  test!(cx, " - Duration", {
    let before = Time::from_unix_ms(1611510414000).to_utc();

    let after = before - Duration::hours(4.0 / 3.0);
    let (hour, minute, second) = after.hms();

    fail::when!(before.date() != after.date(), "Different days.");
    fail::when!(hour != 16);
    fail::when!(minute != 26);
    fail::when!(second != 54);
  });

  test!(cx, " - Self", {
    let before = Time::from_unix_ms(1611510414000).to_utc();
    let after = Time::from_unix_ms(1611510436873).to_utc();

    let elapsed_ms = (after - before).as_ms();
    let backwards = before - after;

    fail::when!(elapsed_ms != 22873.0);
    fail::when!(backwards > Duration::ZERO);
  });
}

/// Tests `Zone`.
fn test_zone(cx: &mut test::Context) {
  test!(cx, "::from_name()", {
    let zone = time::Zone::from_name("America/New_York")?;

    let time = Time::from_unix_ms(1611510414000).to_zone(zone);
    let (year, month, day) = time.date().ymd();
    let (hour, minute, second) = time.hms();

    fail::when!(zone.name() != "America/New_York");
    fail::when!(year != 2021);
    fail::when!(month != 1);
    fail::when!(day != 24);
    fail::when!(hour != 12);
    fail::when!(minute != 46);
    fail::when!(second != 54);
  });

  test!(cx, "::name()", {
    use time::UTC;

    fail::when!(UTC.name() != "UTC");
  });

  test!(cx, "::to_time()", {
    let initial = Date::from_ymd(2021, 1, 24);

    let time = initial.to_time(time::UTC);
    let date = time.date();
    let (hour, minute, second) = time.hms();

    fail::when!(date != initial);
    fail::when!(hour != 0);
    fail::when!(minute != 0);
    fail::when!(second != 0);
  });
}