blob: 5e384b655202c197dbdd467281dadec682817155 [file] [log] [blame] [edit]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/lib/clock/utils.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/clock.h>
#include <zircon/errors.h>
#include <gtest/gtest.h>
#include "src/media/audio/lib/clock/clone_mono.h"
namespace media::audio::clock {
namespace {
TEST(ClockUtilsTest, DuplicateClockIsIdentical) {
zx::clock ref_clock;
auto status =
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &ref_clock);
ASSERT_EQ(status, ZX_OK);
zx::clock duplicate_clock = DuplicateClock(ref_clock);
zx::clock::update_args args;
args.reset().set_value(zx::time(123)).set_rate_adjust(-456);
EXPECT_EQ(ref_clock.update(args), ZX_OK);
zx_clock_details_v1_t clock_details, clock_details_dupe;
EXPECT_EQ(ref_clock.get_details(&clock_details), ZX_OK);
EXPECT_EQ(duplicate_clock.get_details(&clock_details_dupe), ZX_OK);
EXPECT_EQ(clock_details.options, clock_details_dupe.options);
EXPECT_EQ(clock_details.last_value_update_ticks, clock_details_dupe.last_value_update_ticks);
EXPECT_EQ(clock_details.last_rate_adjust_update_ticks,
clock_details_dupe.last_rate_adjust_update_ticks);
EXPECT_EQ(clock_details.generation_counter, clock_details_dupe.generation_counter);
EXPECT_EQ(clock_details.reference_to_synthetic.reference_offset,
clock_details_dupe.reference_to_synthetic.reference_offset);
EXPECT_EQ(clock_details.reference_to_synthetic.synthetic_offset,
clock_details_dupe.reference_to_synthetic.synthetic_offset);
EXPECT_EQ(clock_details.reference_to_synthetic.rate.synthetic_ticks,
clock_details_dupe.reference_to_synthetic.rate.synthetic_ticks);
EXPECT_EQ(clock_details.reference_to_synthetic.rate.reference_ticks,
clock_details_dupe.reference_to_synthetic.rate.reference_ticks);
}
TEST(ClockUtilsTest, DuplicateClockReadable) {
zx::clock ref_clock = audio::clock::CloneOfMonotonic();
EXPECT_TRUE(ref_clock.is_valid());
zx_time_t now;
EXPECT_EQ(ref_clock.read(&now), ZX_OK);
zx::clock duplicate_clock = DuplicateClock(ref_clock);
zx_time_t now2;
EXPECT_EQ(duplicate_clock.read(&now2), ZX_OK);
EXPECT_GT(now2, now);
}
TEST(ClockUtilsTest, DuplicateClockNotWritable) {
zx::clock ref_clock;
auto status =
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &ref_clock);
EXPECT_EQ(status, ZX_OK);
// Reference clock is not yet started.
zx_time_t now;
EXPECT_EQ(ref_clock.read(&now), ZX_OK);
EXPECT_EQ(now, 0);
// Remove writing right.
zx::clock duplicate_clock =
DuplicateClock(ref_clock, ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER | ZX_RIGHT_READ);
zx::clock::update_args args;
args.reset().set_value(zx::clock::get_monotonic());
EXPECT_NE(duplicate_clock.update(args), ZX_OK);
// Duplicate clock is not yet started.
EXPECT_EQ(duplicate_clock.read(&now), ZX_OK);
EXPECT_EQ(now, 0);
// Reference clock can be updated.
EXPECT_EQ(ref_clock.update(args), ZX_OK);
// Duplicate clock is now started.
EXPECT_EQ(duplicate_clock.read(&now), ZX_OK);
EXPECT_GT(now, 0);
}
TEST(ClockUtilsTest, DuplicateClockCanBeDuplicated) {
zx::clock ref_clock = audio::clock::CloneOfMonotonic();
EXPECT_TRUE(ref_clock.is_valid());
zx::clock duplicate_clock = DuplicateClock(ref_clock);
zx::clock duplicate_of_duplicate_clock = DuplicateClock(duplicate_clock);
zx_time_t now;
EXPECT_EQ(duplicate_of_duplicate_clock.read(&now), ZX_OK);
EXPECT_GT(now, 0);
}
// With an uninitialized clock, GetAndDisplayClockDetails should not succeed
TEST(ClockUtilsTest, GetAndDisplayClockDetailsBadHandle) {
zx::clock uninitialized_clock;
auto result = GetClockDetails(uninitialized_clock);
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ZX_ERR_INVALID_ARGS);
auto status = GetAndDisplayClockDetails(uninitialized_clock);
EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
}
// SnapshotClock wraps clock::get_details and converts to a TimelineFunction
TEST(ClockUtilsTest, SnapshotClock) {
zx::clock ref_clock;
auto status =
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &ref_clock);
EXPECT_EQ(status, ZX_OK);
// update starts the clock. Must use a rate_adjust that (when added to 1000000) isn't reduceable
zx::clock::update_args args;
args.reset().set_value(zx::time(0)).set_rate_adjust(+999);
EXPECT_EQ(ref_clock.update(args), ZX_OK);
zx_clock_details_v1_t clock_details;
EXPECT_EQ(ref_clock.get_details(&clock_details), ZX_OK);
DisplayClockDetails(clock_details);
auto snapshot_result = SnapshotClock(ref_clock);
EXPECT_TRUE(snapshot_result.is_ok());
auto snapshot = snapshot_result.take_value();
EXPECT_EQ(clock_details.generation_counter, snapshot.generation);
auto mono_to_ref = snapshot.reference_to_monotonic.Inverse();
EXPECT_EQ(clock_details.reference_to_synthetic.synthetic_offset, mono_to_ref.subject_time());
EXPECT_EQ(clock_details.reference_to_synthetic.reference_offset, mono_to_ref.reference_time());
EXPECT_EQ(clock_details.reference_to_synthetic.rate.synthetic_ticks, mono_to_ref.subject_delta());
EXPECT_EQ(clock_details.reference_to_synthetic.rate.reference_ticks,
mono_to_ref.reference_delta());
}
// Bracket a call to reference_clock.read, with two get_monotonic calls.
// The translated reference-clock time should be within the two monotonic values.
void PredictMonotonicTime(zx::clock& ref_clock) {
zx_time_t now_ref;
zx::time before_mono, after_mono, predicted_mono;
zx_status_t status;
before_mono = zx::clock::get_monotonic();
status = ref_clock.read(&now_ref);
after_mono = zx::clock::get_monotonic();
EXPECT_EQ(status, ZX_OK);
auto mono_time_result = MonotonicTimeFromReferenceTime(ref_clock, zx::time(now_ref));
EXPECT_TRUE(mono_time_result.is_ok());
predicted_mono = mono_time_result.take_value();
EXPECT_GT(predicted_mono, before_mono) << "Predicted monotonic time too small.";
EXPECT_LT(predicted_mono, after_mono) << "Predicted monotonic time too large.";
}
constexpr zx::duration kWaitDuration = zx::msec(35);
TEST(ClockUtilsTest, RefToMonoTime) {
zx::clock ref_clock;
zx_status_t status =
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &ref_clock);
EXPECT_EQ(status, ZX_OK);
zx::clock::update_args args;
args.reset().set_value(zx::time(0)).set_rate_adjust(-1000);
status = ref_clock.update(args);
EXPECT_EQ(status, ZX_OK);
PredictMonotonicTime(ref_clock);
for (auto repeats = 3u; repeats > 0; --repeats) {
zx::nanosleep(zx::deadline_after(kWaitDuration));
PredictMonotonicTime(ref_clock);
}
}
// Bracket a call to get_monotonic, with two reference_clock.read calls.
// The translated monotonic time should be within the two reference_clock values.
void PredictReferenceTime(zx::clock& ref_clock) {
zx_time_t before_ref, after_ref, predicted_ref;
zx::time now_mono;
zx_status_t status;
ref_clock.read(&before_ref);
now_mono = zx::clock::get_monotonic();
status = ref_clock.read(&after_ref);
EXPECT_EQ(status, ZX_OK);
auto ref_time_result = ReferenceTimeFromMonotonicTime(ref_clock, now_mono);
EXPECT_TRUE(ref_time_result.is_ok());
predicted_ref = ref_time_result.take_value().get();
EXPECT_GT(predicted_ref, before_ref) << "Predicted reference time too small.";
EXPECT_LT(predicted_ref, after_ref) << "Predicted reference time too large.";
}
TEST(ClockUtilsTest, MonoToRefTime) {
zx::clock ref_clock;
zx_status_t status =
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &ref_clock);
EXPECT_EQ(status, ZX_OK);
zx::clock::update_args args;
args.reset().set_value(zx::time(987'654'321));
status = ref_clock.update(args);
EXPECT_EQ(status, ZX_OK);
PredictReferenceTime(ref_clock);
for (auto repeats = 3u; repeats > 0; --repeats) {
zx::nanosleep(zx::deadline_after(kWaitDuration));
PredictReferenceTime(ref_clock);
}
}
// Make alternating readings from clock_A and clock_B: call them A1, B2, A3, B4.
// Translate B2 into clock_A's timeline as predict_A2, and A3 to clock_B's timeline as predict_B3.
// We expect strict sequencing of [time_A1,predict_A2,time_A3] and [time_B2,predict_B3,time_B4].
void PredictBetweenReferenceClocks(zx::clock& clock_A, zx::clock& clock_B) {
zx::time time_A1, time_B2, time_A3, time_B4;
clock_A.read(time_A1.get_address());
clock_B.read(time_B2.get_address());
clock_A.read(time_A3.get_address());
clock_B.read(time_B4.get_address());
auto predict_A2 = ReferenceTimeFromReferenceTime(clock_B, time_B2, clock_A).take_value();
EXPECT_GT(predict_A2, time_A1) << "Translated reference time too small.";
EXPECT_LT(predict_A2, time_A3) << "Translated reference time too large.";
auto predict_B3 = ReferenceTimeFromReferenceTime(clock_A, time_A3, clock_B).take_value();
EXPECT_GT(predict_B3, time_B2) << "Translated reference time too small.";
EXPECT_LT(predict_B3, time_B4) << "Translated reference time too large.";
}
TEST(ClockUtilsTest, RefToRefTime) {
zx::clock ref_clock_A, ref_clock_B;
ASSERT_EQ(
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS | ZX_CLOCK_OPT_AUTO_START,
nullptr, &ref_clock_A),
ZX_OK);
ASSERT_EQ(
zx::clock::create(ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &ref_clock_B),
ZX_OK);
zx::clock::update_args args_A, args_B;
args_A.reset().set_rate_adjust(-1000);
args_B.reset().set_value(zx::time(987'654'321)).set_rate_adjust(1000);
ASSERT_EQ(ref_clock_A.update(args_A), ZX_OK);
ASSERT_EQ(ref_clock_B.update(args_B), ZX_OK);
PredictBetweenReferenceClocks(ref_clock_A, ref_clock_B);
for (auto repeats = 3u; repeats > 0; --repeats) {
zx::nanosleep(zx::deadline_after(kWaitDuration));
PredictBetweenReferenceClocks(ref_clock_A, ref_clock_B);
}
}
TEST(ClockUtilsTest, TimelineToAffine) {
auto tl_function = TimelineFunction(2, 3, 5, 7);
auto affine_transform = ToAffineTransform(tl_function);
EXPECT_EQ(affine_transform.a_offset(), tl_function.reference_time());
EXPECT_EQ(affine_transform.b_offset(), tl_function.subject_time());
EXPECT_EQ(affine_transform.numerator(), tl_function.subject_delta());
EXPECT_EQ(affine_transform.denominator(), tl_function.reference_delta());
}
TEST(ClockUtilsTest, AffineToTimeline) {
auto affine_transform = affine::Transform(11, 13, affine::Ratio(17, 19));
auto tl_function = ToTimelineFunction(affine_transform);
EXPECT_EQ(affine_transform.a_offset(), tl_function.reference_time());
EXPECT_EQ(affine_transform.b_offset(), tl_function.subject_time());
EXPECT_EQ(affine_transform.numerator(), tl_function.subject_delta());
EXPECT_EQ(affine_transform.denominator(), tl_function.reference_delta());
}
} // namespace
} // namespace media::audio::clock