1335 lines
57 KiB
C++
1335 lines
57 KiB
C++
/* Copyright 2019 Google LLC. This file and proprietary
|
|
* source code may only be used and distributed under the Widevine
|
|
* License Agreement.
|
|
*/
|
|
|
|
#include "OEMCryptoCENCCommon.h"
|
|
#include "gtest/gtest.h"
|
|
#include "odk.h"
|
|
#include "odk_structs.h"
|
|
#include "odk_structs_priv.h"
|
|
#include "odk_test_helper.h"
|
|
|
|
namespace {
|
|
|
|
// The rental clock starts when the request is signed. If any test fails
|
|
// because a time is off by exactly 1000, that is a strong indication that we
|
|
// confused rental and system clocks. The system clock should only be used
|
|
// internally on the device, because it might not be synchronized from one
|
|
// device to another. Rental clock is used in the license message from the
|
|
// server.
|
|
constexpr uint64_t kRentalClockStart = 1000u;
|
|
|
|
// The renewal grace period is used by the server and the CDM layer to allow
|
|
// time between when the renewal is requested and when playback is cutoff if the
|
|
// renewal is not loaded.
|
|
constexpr uint64_t kGracePeriod = 5u;
|
|
|
|
constexpr uint32_t kExtraPayloadSize = 128u;
|
|
|
|
constexpr uint32_t kSystemTime = 20u;
|
|
|
|
namespace wvodk_test {
|
|
|
|
TEST(OdkTimerBasicTest, ParseLicenseTimerSet) {
|
|
// playback timer is successfully started
|
|
::wvodk_test::ODK_LicenseResponseParams params;
|
|
ODK_SetDefaultLicenseResponseParams(¶ms, ODK_MAJOR_VERSION);
|
|
params.parsed_license.renewal_delay_base = OEMCrypto_License_Load;
|
|
params.parsed_license.timer_limits.soft_enforce_rental_duration = false;
|
|
params.parsed_license.timer_limits.soft_enforce_playback_duration = false;
|
|
params.parsed_license.timer_limits.earliest_playback_start_seconds = 10;
|
|
params.parsed_license.timer_limits.total_playback_duration_seconds = 0;
|
|
params.parsed_license.timer_limits.rental_duration_seconds = 10;
|
|
params.parsed_license.timer_limits.initial_renewal_duration_seconds = 0;
|
|
OEMCryptoResult result =
|
|
ODK_InitializeClockValues(¶ms.clock_values, kSystemTime);
|
|
EXPECT_EQ(OEMCrypto_SUCCESS, result);
|
|
params.clock_values.time_of_license_request_signed = 5;
|
|
params.clock_values.status = kActive;
|
|
|
|
uint8_t* buf = nullptr;
|
|
uint32_t buf_size = 0;
|
|
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
|
&buf_size);
|
|
|
|
result = ODK_ParseLicense(
|
|
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
|
|
params.usage_entry_present, kSystemTime, &(params.timer_limits),
|
|
&(params.clock_values), &(params.core_message.nonce_values),
|
|
&(params.parsed_license), nullptr);
|
|
EXPECT_EQ(ODK_SET_TIMER, result);
|
|
delete[] buf;
|
|
}
|
|
|
|
TEST(OdkTimerBasicTest, ParseLicenseTimerDisabled) {
|
|
// playback timer is successfully started
|
|
::wvodk_test::ODK_LicenseResponseParams params;
|
|
ODK_SetDefaultLicenseResponseParams(¶ms, ODK_MAJOR_VERSION);
|
|
params.parsed_license.renewal_delay_base = OEMCrypto_License_Load;
|
|
params.parsed_license.timer_limits.soft_enforce_rental_duration = true;
|
|
params.parsed_license.timer_limits.earliest_playback_start_seconds = 3;
|
|
params.parsed_license.timer_limits.total_playback_duration_seconds = 0;
|
|
params.parsed_license.timer_limits.initial_renewal_duration_seconds = 0;
|
|
params.clock_values.time_of_first_decrypt = 10;
|
|
params.clock_values.time_of_license_request_signed = 5;
|
|
params.clock_values.status = kActive;
|
|
|
|
uint8_t* buf = nullptr;
|
|
uint32_t buf_size = 0;
|
|
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
|
&buf_size);
|
|
|
|
OEMCryptoResult result = ODK_ParseLicense(
|
|
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
|
|
params.usage_entry_present, kSystemTime, &(params.timer_limits),
|
|
&(params.clock_values), &(params.core_message.nonce_values),
|
|
&(params.parsed_license), nullptr);
|
|
EXPECT_EQ(ODK_DISABLE_TIMER, result);
|
|
delete[] buf;
|
|
}
|
|
|
|
TEST(OdkTimerBasicTest, ParseRenewalTimerExpired) {
|
|
// playback timer is successfully started
|
|
::wvodk_test::ODK_LicenseResponseParams params;
|
|
ODK_SetDefaultLicenseResponseParams(¶ms, ODK_MAJOR_VERSION);
|
|
|
|
params.parsed_license.renewal_delay_base = OEMCrypto_License_Load;
|
|
params.parsed_license.timer_limits.rental_duration_seconds = 5;
|
|
params.parsed_license.timer_limits.earliest_playback_start_seconds = 3;
|
|
OEMCryptoResult result =
|
|
ODK_InitializeClockValues(¶ms.clock_values, kSystemTime);
|
|
EXPECT_EQ(OEMCrypto_SUCCESS, result);
|
|
params.clock_values.time_of_license_request_signed = 5;
|
|
|
|
uint8_t* buf = nullptr;
|
|
uint32_t buf_size = 0;
|
|
ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf,
|
|
&buf_size);
|
|
|
|
result = ODK_ParseLicense(
|
|
buf, buf_size + kExtraPayloadSize, buf_size, params.initial_license_load,
|
|
params.usage_entry_present, kSystemTime, &(params.timer_limits),
|
|
&(params.clock_values), &(params.core_message.nonce_values),
|
|
&(params.parsed_license), nullptr);
|
|
EXPECT_EQ(ODK_TIMER_EXPIRED, result);
|
|
delete[] buf;
|
|
}
|
|
|
|
} // namespace wvodk_test
|
|
|
|
TEST(OdkTimerBasicTest, NullTest) {
|
|
// Assert that nullptr does not cause a core dump.
|
|
ODK_InitializeClockValues(nullptr, 0u);
|
|
ODK_ReloadClockValues(nullptr, 0u, 0u, 0u, kActive, 0u);
|
|
ODK_AttemptFirstPlayback(0u, nullptr, nullptr, nullptr);
|
|
ODK_UpdateLastPlaybackTime(0, nullptr, nullptr);
|
|
ASSERT_TRUE(true);
|
|
}
|
|
|
|
TEST(OdkTimerBasicTest, Init) {
|
|
// Verify that basic initialization sets all of the fields.
|
|
ODK_ClockValues clock_values;
|
|
memset(&clock_values, 0, sizeof(clock_values));
|
|
uint64_t time = 42;
|
|
ODK_InitializeClockValues(&clock_values, time);
|
|
EXPECT_EQ(clock_values.time_of_license_request_signed, time);
|
|
EXPECT_EQ(clock_values.time_of_first_decrypt, 0u);
|
|
EXPECT_EQ(clock_values.time_of_last_decrypt, 0u);
|
|
EXPECT_EQ(clock_values.time_when_timer_expires, 0u);
|
|
EXPECT_EQ(clock_values.timer_status,
|
|
ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED);
|
|
EXPECT_EQ(clock_values.status, kUnused);
|
|
}
|
|
|
|
TEST(OdkTimerBasicTest, Reload) {
|
|
// Verify that reloading clock values uses the same values
|
|
// for fields that can be saved, and sets others to 0.
|
|
ODK_ClockValues clock_values;
|
|
memset(&clock_values, 0, sizeof(clock_values));
|
|
uint64_t time = 42u;
|
|
uint64_t lic_signed = 1u;
|
|
uint64_t first_decrypt = 2u;
|
|
uint64_t last_decrypt = 3u;
|
|
enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed;
|
|
ODK_ReloadClockValues(&clock_values, lic_signed, first_decrypt, last_decrypt,
|
|
status, time);
|
|
EXPECT_EQ(clock_values.time_of_license_request_signed, lic_signed);
|
|
EXPECT_EQ(clock_values.time_of_first_decrypt, first_decrypt);
|
|
EXPECT_EQ(clock_values.time_of_last_decrypt, last_decrypt);
|
|
EXPECT_EQ(clock_values.time_when_timer_expires, 0u);
|
|
EXPECT_EQ(clock_values.timer_status,
|
|
ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED);
|
|
EXPECT_EQ(clock_values.status, status);
|
|
}
|
|
|
|
// All of the following test cases are derived from this base class. It
|
|
// simulates loading a license, attempting playback, and reloading a license.
|
|
class ODKTimerTest : public ::testing::Test {
|
|
public:
|
|
ODKTimerTest() {
|
|
// These are reasonable initial values for most tests. This is an unlimited
|
|
// license. Most of the tests below will add some restrictions by changing
|
|
// these values, and then verify that the ODK library behaves correctly.
|
|
timer_limits_.soft_enforce_rental_duration = true;
|
|
timer_limits_.soft_enforce_playback_duration = true;
|
|
timer_limits_.earliest_playback_start_seconds = 0u;
|
|
// A duration of 0 means infinite.
|
|
timer_limits_.rental_duration_seconds = 0u;
|
|
timer_limits_.total_playback_duration_seconds = 0u;
|
|
timer_limits_.initial_renewal_duration_seconds = 0u;
|
|
|
|
// This is when we will attempt the first valid playback.
|
|
start_of_playback_ =
|
|
GetSystemTime(timer_limits_.earliest_playback_start_seconds + 10);
|
|
}
|
|
|
|
protected:
|
|
void SetUp() override {
|
|
::testing::Test::SetUp();
|
|
// Start rental clock at kRentalClockStart. This happens when the license
|
|
// request is signed.
|
|
ODK_InitializeClockValues(&clock_values_, kRentalClockStart);
|
|
EXPECT_EQ(clock_values_.time_of_license_request_signed, kRentalClockStart);
|
|
}
|
|
|
|
// Simulate loading or reloading a license in a new session. An offline
|
|
// license should have several of the clock_value's fields saved into the
|
|
// usage table. When it is reloaded those values should be reloaded. From
|
|
// these fields, the ODK function can tell if this is the first playback for
|
|
// the license, or just the first playback for this session. The key fields
|
|
// that should be saved are the status, and the times for license signed, and
|
|
// first and last playback times.
|
|
void ReloadLicense(uint64_t system_time) {
|
|
ODK_ClockValues old_clock_values = clock_values_;
|
|
// First clear out the old clock values.
|
|
memset(&clock_values_, 0, sizeof(clock_values_));
|
|
// When the session is opened, the clock values are initialized.
|
|
ODK_InitializeClockValues(&clock_values_, 0);
|
|
// When the usage entry is reloaded, the clock values are reloaded.
|
|
ODK_ReloadClockValues(&clock_values_,
|
|
old_clock_values.time_of_license_request_signed,
|
|
old_clock_values.time_of_first_decrypt,
|
|
old_clock_values.time_of_last_decrypt,
|
|
old_clock_values.status, system_time);
|
|
EXPECT_EQ(clock_values_.timer_status,
|
|
ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED);
|
|
// These shall not change:
|
|
EXPECT_EQ(clock_values_.time_of_license_request_signed, kRentalClockStart);
|
|
EXPECT_EQ(clock_values_.time_of_first_decrypt,
|
|
old_clock_values.time_of_first_decrypt);
|
|
EXPECT_EQ(clock_values_.time_of_last_decrypt,
|
|
old_clock_values.time_of_last_decrypt);
|
|
// ODK_ParseLicense sets the new timer state.
|
|
clock_values_.timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED;
|
|
}
|
|
|
|
// Simulate loading or reloading a license, then verify that we are allowed
|
|
// playback from |start| to |stop|. If |cutoff| is not 0, then expect the
|
|
// timer to expire at that time. If |cutoff| is 0, expect the timer is not
|
|
// set. If you refer to the diagrams in "License Duration and Renewal", this
|
|
// tests a cyan bar with a green check mark.
|
|
// When nonzero |start|, |stop|, and |cutoff| are all system times.
|
|
void LoadAndAllowPlayback(uint64_t start, uint64_t stop, uint64_t cutoff) {
|
|
ReloadLicense(start);
|
|
AllowPlayback(start, stop, cutoff);
|
|
}
|
|
|
|
// Verify that we are allowed playback from |start| to |stop|. If |cutoff| is
|
|
// not 0, then expect the timer to expire at that time. If |cutoff| is 0,
|
|
// expect the timer is not set. If you refer to the diagrams in "License
|
|
// Duration and Renewal", this tests a cyan bar with a green check mark.
|
|
// When nonzero |start|, |stop|, and |cutoff| are all system times.
|
|
void AllowPlayback(uint64_t start, uint64_t stop, uint64_t cutoff) {
|
|
ASSERT_LT(start, stop);
|
|
if (cutoff > 0) ASSERT_LE(stop, cutoff);
|
|
uint64_t timer_value;
|
|
const OEMCryptoResult result = ODK_AttemptFirstPlayback(
|
|
start, &timer_limits_, &clock_values_, &timer_value);
|
|
// After first playback, the license is active.
|
|
EXPECT_EQ(clock_values_.status, kActive);
|
|
EXPECT_EQ(clock_values_.time_when_timer_expires, cutoff);
|
|
if (cutoff > 0) { // If we expect the timer to be set.
|
|
EXPECT_EQ(result, ODK_SET_TIMER);
|
|
EXPECT_EQ(timer_value, cutoff - start);
|
|
} else {
|
|
EXPECT_EQ(result, ODK_DISABLE_TIMER);
|
|
}
|
|
CheckClockValues(start);
|
|
const uint64_t mid = (start + stop) / 2;
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
|
|
OEMCrypto_SUCCESS);
|
|
CheckClockValues(mid);
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
|
|
OEMCrypto_SUCCESS);
|
|
CheckClockValues(stop);
|
|
}
|
|
|
|
// Simulate loading or reloading a license, then attempt to play from |start|
|
|
// to |cutoff|. Verify that we are allowed playback from |start| to |cutoff|,
|
|
// but playback is not allowed after |cutoff|. If you refer to the diagrams in
|
|
// "License Duration and Renewal", this tests a cyan bar with a black X. When
|
|
// nonzero |start|, and |cutoff| are all system times.
|
|
void LoadAndTerminatePlayback(uint64_t start, uint64_t cutoff) {
|
|
ReloadLicense(start);
|
|
TerminatePlayback(start, cutoff, cutoff + 10);
|
|
}
|
|
|
|
// Attempt to play from |start| to |stop|. Verify that we are allowed playback
|
|
// from |start| to |cutoff|, but playback not allowed after |cutoff|. If you
|
|
// refer to the diagrams in "License Duration and Renewal", this tests a cyan
|
|
// bar with a black X. This assumes that |cutoff| is before |stop|.
|
|
// When nonzero |start|, |stop|, and |cutoff| are all system times.
|
|
void TerminatePlayback(uint64_t start, uint64_t cutoff, uint64_t stop) {
|
|
ASSERT_LT(start, cutoff);
|
|
ASSERT_LT(cutoff, stop);
|
|
AllowPlayback(start, cutoff, cutoff);
|
|
EXPECT_EQ(
|
|
ODK_UpdateLastPlaybackTime(cutoff + 1, &timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
CheckClockValues(cutoff);
|
|
const uint64_t mid = (cutoff + stop) / 2;
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
// times do not change if playback was not allowed.
|
|
CheckClockValues(cutoff);
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
CheckClockValues(cutoff);
|
|
}
|
|
|
|
// Verify that we are not allowed playback at the |start| time. If you refer
|
|
// to the diagrams in "License Duration and Renewal", this tests a cyan line
|
|
// followed by a black X. The parameter |start| is system time.
|
|
void ForbidPlayback(uint64_t start) {
|
|
ReloadLicense(start);
|
|
ODK_ClockValues old_clock_values = clock_values_;
|
|
uint64_t timer_value;
|
|
EXPECT_EQ(ODK_AttemptFirstPlayback(start, &timer_limits_, &clock_values_,
|
|
&timer_value),
|
|
ODK_TIMER_EXPIRED);
|
|
// These should not have changed. In particular, if the license was unused
|
|
// before, it should reamin unused.
|
|
EXPECT_EQ(clock_values_.time_of_license_request_signed,
|
|
old_clock_values.time_of_license_request_signed);
|
|
EXPECT_EQ(clock_values_.time_of_first_decrypt,
|
|
old_clock_values.time_of_first_decrypt);
|
|
EXPECT_EQ(clock_values_.time_of_last_decrypt,
|
|
old_clock_values.time_of_last_decrypt);
|
|
EXPECT_EQ(clock_values_.status, old_clock_values.status);
|
|
}
|
|
|
|
// Verify that the clock values are correct.
|
|
void CheckClockValues(uint64_t time_of_last_decrypt) {
|
|
EXPECT_EQ(clock_values_.time_of_license_request_signed, kRentalClockStart);
|
|
EXPECT_EQ(clock_values_.time_of_first_decrypt, start_of_playback_);
|
|
EXPECT_EQ(clock_values_.time_of_last_decrypt, time_of_last_decrypt);
|
|
EXPECT_EQ(clock_values_.status, kActive);
|
|
}
|
|
|
|
// Convert from rental time to system time. By "system time", we mean
|
|
// OEMCrypto's montonic clock. The spec does not specify a starting time for
|
|
// this clock.
|
|
uint64_t GetSystemTime(uint64_t rental_clock) {
|
|
return kRentalClockStart + rental_clock;
|
|
}
|
|
|
|
// The end of the playback window. (using system clock)
|
|
// This is not useful if the playback duration is 0.
|
|
uint64_t EndOfPlaybackWindow() {
|
|
return start_of_playback_ + timer_limits_.total_playback_duration_seconds;
|
|
}
|
|
|
|
// The end of the rental window. (using system clock)
|
|
// This is not useful if the rental duration is 0.
|
|
uint64_t EndOfRentalWindow() {
|
|
return GetSystemTime(timer_limits_.earliest_playback_start_seconds) +
|
|
timer_limits_.rental_duration_seconds;
|
|
}
|
|
|
|
ODK_TimerLimits timer_limits_;
|
|
ODK_ClockValues clock_values_;
|
|
// The start of playback. This is set to the planned start at the beginning of
|
|
// the test. (using system clock)
|
|
uint64_t start_of_playback_;
|
|
};
|
|
|
|
TEST_F(ODKTimerTest, SimplePlayback) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100, 0);
|
|
}
|
|
|
|
// This tests that we are not allowed to start playback before the rental window
|
|
// opens.
|
|
TEST_F(ODKTimerTest, EarlyTest) {
|
|
timer_limits_.earliest_playback_start_seconds = 100u;
|
|
// This is earlier than we are allowed.
|
|
const uint64_t bad_start_time =
|
|
GetSystemTime(timer_limits_.earliest_playback_start_seconds - 10);
|
|
// An attempt to start playback before the timer starts is an error.
|
|
// We use the TIMER_EXPIRED error to mean both early or late.
|
|
ForbidPlayback(bad_start_time);
|
|
// And times were not updated:
|
|
EXPECT_EQ(clock_values_.status, kUnused);
|
|
// This is when we will attempt the first valid playback.
|
|
start_of_playback_ =
|
|
GetSystemTime(timer_limits_.earliest_playback_start_seconds + 10);
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100, 0);
|
|
}
|
|
|
|
// This runs the same test as above, but explicitly gives the ODK library a null
|
|
// pointer for the timer value. A null pointer is allowed for OEMCrypto
|
|
// implementations that do not manage their own timer.
|
|
TEST_F(ODKTimerTest, NullTimer) {
|
|
timer_limits_.earliest_playback_start_seconds = 100u;
|
|
// This is the earlier, invalid start time.
|
|
const uint64_t bad_start_time =
|
|
GetSystemTime(timer_limits_.earliest_playback_start_seconds - 10);
|
|
timer_limits_.rental_duration_seconds = 300;
|
|
timer_limits_.soft_enforce_rental_duration = false;
|
|
ReloadLicense(GetSystemTime(bad_start_time));
|
|
|
|
// If OEMCrypto passes in a null pointer, then the ODK should allow this.
|
|
uint64_t* timer_value_pointer = nullptr;
|
|
EXPECT_EQ(ODK_AttemptFirstPlayback(bad_start_time, &timer_limits_,
|
|
&clock_values_, timer_value_pointer),
|
|
ODK_TIMER_EXPIRED);
|
|
start_of_playback_ =
|
|
GetSystemTime(timer_limits_.earliest_playback_start_seconds + 10);
|
|
EXPECT_EQ(ODK_AttemptFirstPlayback(start_of_playback_, &timer_limits_,
|
|
&clock_values_, timer_value_pointer),
|
|
ODK_SET_TIMER);
|
|
CheckClockValues(start_of_playback_);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Note on Use Case tests. The test classes below correspond to the use cases
|
|
// in the doucment "License Duration and Renewal.". Each diagram in that
|
|
// document has a test class below to verify the use case is supported.
|
|
//
|
|
// In the document, we use realistic rental times in hours or days. In these
|
|
// tests, we will use round numbers so that it is easier to read. For example,
|
|
// instead of a seven day rental duration, we will use a 700 rental duration.
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
// Streaming is the simplest use case. The user has three hours to watch the
|
|
// movie from the time of the rental. (See above for note on Use Case tests)
|
|
class ODKUseCase_Streaming : public ODKTimerTest {
|
|
public:
|
|
ODKUseCase_Streaming() {
|
|
// Rental duration = 3 hours hard. (use 300 for readability)
|
|
// Playback duration = 0 (unlimited)
|
|
timer_limits_.soft_enforce_rental_duration = false;
|
|
timer_limits_.rental_duration_seconds = 300;
|
|
timer_limits_.total_playback_duration_seconds = 0;
|
|
}
|
|
};
|
|
|
|
// Playback within rental duration.
|
|
TEST_F(ODKUseCase_Streaming, Case1) {
|
|
// Allow playback within the rental window.
|
|
LoadAndAllowPlayback(start_of_playback_, EndOfRentalWindow(),
|
|
EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_Streaming, Case2) {
|
|
// Allow playback within the rental window, but not beyond.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within rental duration, last one exceeds rental
|
|
// duration.
|
|
TEST_F(ODKUseCase_Streaming, Case3) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfRentalWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 110, start_of_playback_ + 120,
|
|
EndOfRentalWindow());
|
|
LoadAndTerminatePlayback(start_of_playback_ + 200, EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback within rental duration, restart exceeds rental duration.
|
|
TEST_F(ODKUseCase_Streaming, Case4) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfRentalWindow());
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
// Initial playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_Streaming, Case5) {
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Streaming Quick Start. The user must start watching within 30 seconds, and
|
|
// then has three hours to finish. (See above for note on Use Case tests)
|
|
class ODKUseCase_StreamingQuickStart : public ODKTimerTest {
|
|
public:
|
|
ODKUseCase_StreamingQuickStart() {
|
|
// Rental duration = 30 seconds, soft.
|
|
// Playback duration = 3 hours (use 300 for readability)
|
|
timer_limits_.soft_enforce_rental_duration = true;
|
|
timer_limits_.rental_duration_seconds = 30;
|
|
timer_limits_.total_playback_duration_seconds = 300;
|
|
timer_limits_.soft_enforce_playback_duration = false;
|
|
|
|
// A valid start of playback time.
|
|
start_of_playback_ =
|
|
GetSystemTime(timer_limits_.rental_duration_seconds - 10);
|
|
}
|
|
};
|
|
|
|
// Playback starts within rental duration, continues within playback duration.
|
|
TEST_F(ODKUseCase_StreamingQuickStart, Case1) {
|
|
// As seen in the drawing, the playback window exceeds the rental window.
|
|
EXPECT_LE(EndOfRentalWindow(), EndOfPlaybackWindow());
|
|
// Allow playback within the playback window.
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
|
|
EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback exceeds playback duration.
|
|
TEST_F(ODKUseCase_StreamingQuickStart, Case2) {
|
|
// Allow playback within the playback window, but not beyond.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// playback duration.
|
|
TEST_F(ODKUseCase_StreamingQuickStart, Case3) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfPlaybackWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 110, start_of_playback_ + 120,
|
|
EndOfPlaybackWindow());
|
|
LoadAndTerminatePlayback(start_of_playback_ + 200, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Initial playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_StreamingQuickStart, Case4) {
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Seven Day / Two Day. The user must start watching within 7 days. Once
|
|
// started, the user has two days to finish the video. Playback is cutoff by the
|
|
// smaller of the two time limits. The first four cases start on day
|
|
// three. (See above for note on Use Case tests)
|
|
class ODKUseCase_SevenHardTwoHard_Start3 : public ODKTimerTest {
|
|
public:
|
|
ODKUseCase_SevenHardTwoHard_Start3() {
|
|
// Rental duration = 700, hard
|
|
// Playback duration = 200, hard
|
|
timer_limits_.rental_duration_seconds = 700;
|
|
timer_limits_.soft_enforce_rental_duration = false;
|
|
timer_limits_.total_playback_duration_seconds = 200;
|
|
timer_limits_.soft_enforce_playback_duration = false;
|
|
|
|
start_of_playback_ = GetSystemTime(300);
|
|
}
|
|
};
|
|
|
|
// Playback within playback and rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case1) {
|
|
// As seen in the drawing, the playback window is within the rental window.
|
|
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback within the rental window.
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
|
|
EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case2) {
|
|
// Allow playback within the playback window, but not beyond.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case3) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfPlaybackWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100,
|
|
EndOfPlaybackWindow());
|
|
LoadAndTerminatePlayback(start_of_playback_ + 125, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Restart exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start3, Case4) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfPlaybackWindow());
|
|
ForbidPlayback(EndOfPlaybackWindow() + 10);
|
|
}
|
|
|
|
// The next four cases start on day six.
|
|
class ODKUseCase_SevenHardTwoHard_Start6
|
|
: public ODKUseCase_SevenHardTwoHard_Start3 {
|
|
public:
|
|
ODKUseCase_SevenHardTwoHard_Start6() {
|
|
start_of_playback_ = GetSystemTime(600);
|
|
}
|
|
};
|
|
|
|
// Playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case5) {
|
|
// As seen in the drawing, the playback window is exceeds the rental window.
|
|
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback within the rental window.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one is terminated
|
|
// at the end of the rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case6) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
|
|
EndOfRentalWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75,
|
|
EndOfRentalWindow());
|
|
// Allow playback that starts within rental window, but terminate at end of
|
|
// rental window.
|
|
LoadAndTerminatePlayback(start_of_playback_ + 90, EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback within playback duration, restart exceeds rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case7) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
|
|
EndOfRentalWindow());
|
|
// Restart does not work after end of playback window.
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
// Playback exceeds rental and playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case8) {
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
|
|
}
|
|
|
|
// First playback cannot exceed rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoHard_Start6, Case9) {
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Seven Day / Two Day. The user must start watching within 7 days. Once
|
|
// started, the user has two days to finish the video. Playback is cutoff by the
|
|
// rental duration time limits. The first four cases start on day three. (See
|
|
// above for note on Use Case tests)
|
|
class ODKUseCase_SevenHardTwoSoft_Start3 : public ODKTimerTest {
|
|
public:
|
|
ODKUseCase_SevenHardTwoSoft_Start3() {
|
|
// Rental duration = 700, hard
|
|
// Playback duration = 200, hard
|
|
timer_limits_.rental_duration_seconds = 700;
|
|
timer_limits_.soft_enforce_rental_duration = false;
|
|
timer_limits_.total_playback_duration_seconds = 200;
|
|
timer_limits_.soft_enforce_playback_duration = true;
|
|
|
|
start_of_playback_ = GetSystemTime(300);
|
|
}
|
|
};
|
|
|
|
// Playback within playback and rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case1) {
|
|
// As seen in the drawing, the playback window is within the rental window.
|
|
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback within the rental window.
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
|
|
EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case2) {
|
|
// Allow playback within the playback window, and a little after.
|
|
// Timer expires at end of rental window.
|
|
LoadAndAllowPlayback(start_of_playback_, EndOfPlaybackWindow() + 50,
|
|
EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case3) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfRentalWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100,
|
|
EndOfRentalWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 125, EndOfPlaybackWindow() + 50,
|
|
EndOfRentalWindow());
|
|
}
|
|
|
|
// Restart exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start3, Case4) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfRentalWindow());
|
|
ForbidPlayback(EndOfPlaybackWindow() + 10);
|
|
}
|
|
|
|
// The next four cases start on day six.
|
|
class ODKUseCase_SevenHardTwoSoft_Start6
|
|
: public ODKUseCase_SevenHardTwoSoft_Start3 {
|
|
public:
|
|
ODKUseCase_SevenHardTwoSoft_Start6() {
|
|
start_of_playback_ = GetSystemTime(600);
|
|
}
|
|
};
|
|
|
|
// Playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case5) {
|
|
// As seen in the drawing, the playback window is exceeds the rental window.
|
|
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback within the rental window.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case6) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
|
|
EndOfRentalWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75,
|
|
EndOfRentalWindow());
|
|
LoadAndTerminatePlayback(start_of_playback_ + 90, EndOfRentalWindow());
|
|
}
|
|
|
|
// Playback within playback duration, restart exceeds rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case7) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
|
|
EndOfRentalWindow());
|
|
// Restart does not work after end of playback window.
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
// Playback exceeds rental and playback duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case8) {
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfRentalWindow());
|
|
}
|
|
|
|
// First playback cannot exceed rental duration.
|
|
TEST_F(ODKUseCase_SevenHardTwoSoft_Start6, Case9) {
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Seven Day / Two Day. The user must start watching within 7 days. Once
|
|
// started, the user has two days to finish the video. Playback is cutoff by the
|
|
// playback duration. The first four cases start on day three. (See above for
|
|
// note on Use Case tests)
|
|
class ODKUseCase_SevenSoftTwoHard_Start3 : public ODKTimerTest {
|
|
public:
|
|
ODKUseCase_SevenSoftTwoHard_Start3() {
|
|
// Rental duration = 700, hard
|
|
// Playback duration = 300, hard
|
|
timer_limits_.rental_duration_seconds = 700;
|
|
timer_limits_.soft_enforce_rental_duration = true;
|
|
timer_limits_.total_playback_duration_seconds = 200;
|
|
timer_limits_.soft_enforce_playback_duration = false;
|
|
|
|
start_of_playback_ = GetSystemTime(300);
|
|
}
|
|
};
|
|
|
|
// Playback within playback and rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case1) {
|
|
// As seen in the drawing, the playback window is within the rental window.
|
|
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback within the rental window.
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100,
|
|
EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case2) {
|
|
// Allow playback within the playback window, but not beyond.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case3) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfPlaybackWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100,
|
|
EndOfPlaybackWindow());
|
|
LoadAndTerminatePlayback(start_of_playback_ + 125, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Restart exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start3, Case4) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50,
|
|
EndOfPlaybackWindow());
|
|
ForbidPlayback(EndOfPlaybackWindow() + 10);
|
|
}
|
|
|
|
// The next four cases start on day six.
|
|
class ODKUseCase_SevenSoftTwoHard_Start6
|
|
: public ODKUseCase_SevenSoftTwoHard_Start3 {
|
|
public:
|
|
ODKUseCase_SevenSoftTwoHard_Start6() {
|
|
start_of_playback_ = GetSystemTime(600);
|
|
}
|
|
};
|
|
|
|
// Playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case5) {
|
|
// As seen in the drawing, the playback window exceeds the rental window.
|
|
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback to continue beyond the rental window, but not beyond the
|
|
// playback window.
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case6) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
|
|
EndOfPlaybackWindow());
|
|
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75,
|
|
EndOfPlaybackWindow());
|
|
LoadAndTerminatePlayback(start_of_playback_ + 90, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Restart exceeds rental duration, playback exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case7) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25,
|
|
EndOfPlaybackWindow());
|
|
LoadAndTerminatePlayback(EndOfRentalWindow() + 10, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Playback exceeds rental and playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case8) {
|
|
LoadAndTerminatePlayback(start_of_playback_, EndOfPlaybackWindow());
|
|
}
|
|
|
|
// First playback cannot exceed rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoHard_Start6, Case9) {
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Seven Day / Two Day. The user must start watching within 7 days. Once
|
|
// started, the user has two days to finish the video. Playback is not cutoff,
|
|
// but restarts are not allowed after playback duration. The first four cases
|
|
// start on day three. (See above for note on Use Case tests)
|
|
class ODKUseCase_SevenSoftTwoSoft_Start3 : public ODKTimerTest {
|
|
public:
|
|
ODKUseCase_SevenSoftTwoSoft_Start3() {
|
|
// Rental duration = 700, hard
|
|
// Playback duration = 200, hard
|
|
timer_limits_.rental_duration_seconds = 700;
|
|
timer_limits_.soft_enforce_rental_duration = true;
|
|
timer_limits_.total_playback_duration_seconds = 200;
|
|
timer_limits_.soft_enforce_playback_duration = true;
|
|
|
|
start_of_playback_ = GetSystemTime(300);
|
|
}
|
|
};
|
|
|
|
// Playback within playback and rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case1) {
|
|
// As seen in the drawing, the playback window is within the rental window.
|
|
EXPECT_LT(EndOfPlaybackWindow(), EndOfRentalWindow());
|
|
// Allow playback within the rental window.
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 100, 0);
|
|
}
|
|
|
|
// Playback exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case2) {
|
|
// Allow playback within the playback window, and beyond. No timer limit.
|
|
LoadAndAllowPlayback(start_of_playback_, EndOfPlaybackWindow() + 50, 0);
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case3) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50, 0);
|
|
LoadAndAllowPlayback(start_of_playback_ + 75, start_of_playback_ + 100, 0);
|
|
LoadAndAllowPlayback(start_of_playback_ + 125, EndOfPlaybackWindow() + 50, 0);
|
|
}
|
|
|
|
// Restart exceeds playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start3, Case4) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 50, 0);
|
|
ForbidPlayback(EndOfPlaybackWindow() + 10);
|
|
}
|
|
|
|
// The next four cases start on day six.
|
|
class ODKUseCase_SevenSoftTwoSoft_Start6
|
|
: public ODKUseCase_SevenSoftTwoSoft_Start3 {
|
|
public:
|
|
ODKUseCase_SevenSoftTwoSoft_Start6() {
|
|
start_of_playback_ = GetSystemTime(600);
|
|
}
|
|
};
|
|
|
|
// Playback exceeds rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case5) {
|
|
// As seen in the drawing, the playback window exceeds the rental window.
|
|
EXPECT_GT(EndOfPlaybackWindow(), EndOfRentalWindow() + 25);
|
|
// Allow playback past the rental window, but within the playback window.
|
|
LoadAndAllowPlayback(start_of_playback_, EndOfRentalWindow() + 25, 0);
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// rental and playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case6) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25, 0);
|
|
LoadAndAllowPlayback(start_of_playback_ + 50, start_of_playback_ + 75, 0);
|
|
LoadAndAllowPlayback(start_of_playback_ + 100, EndOfPlaybackWindow() + 50, 0);
|
|
}
|
|
|
|
// Playback with stops/restarts within playback duration, last one exceeds
|
|
// playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case7) {
|
|
LoadAndAllowPlayback(start_of_playback_, start_of_playback_ + 25, 0);
|
|
// Allow playback to start after end of rental window, and continue after
|
|
// playback window.
|
|
LoadAndAllowPlayback(EndOfRentalWindow() + 10, EndOfPlaybackWindow() + 10, 0);
|
|
// But forbid restart after playback window.
|
|
ForbidPlayback(EndOfPlaybackWindow() + 20);
|
|
}
|
|
|
|
// Playback exceeds rental and playback duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case8) {
|
|
LoadAndAllowPlayback(start_of_playback_, EndOfPlaybackWindow() + 100, 0);
|
|
}
|
|
|
|
// First playback cannot exceed rental duration.
|
|
TEST_F(ODKUseCase_SevenSoftTwoSoft_Start6, Case9) {
|
|
ForbidPlayback(EndOfRentalWindow() + 10);
|
|
}
|
|
|
|
class RenewalTest : public ODKTimerTest {
|
|
protected:
|
|
// Verify that a renewal can be processed and playback may continue from
|
|
// |start| to |stop|. If |cutoff| is not 0, then expect the timer to expire
|
|
// at that time. If |cutoff| is 0, expect the timer is not set. If you refer
|
|
// to the diagrams in "License Duration and Renewal", this tests a cyan bar
|
|
// with a green check mark.
|
|
void RenewAndContinue(uint64_t start, uint64_t stop, uint64_t cutoff,
|
|
uint64_t renewal_duration_seconds) {
|
|
uint64_t timer_value;
|
|
RenewAndContinue(start, stop, cutoff, renewal_duration_seconds,
|
|
&timer_value);
|
|
}
|
|
|
|
// Verify that a renewal can be processed and playback may continue from
|
|
// |start| to |stop|. If |cutoff| is not 0, then expect the timer to expire
|
|
// at that time. If |cutoff| is 0, expect the timer is not set. If you refer
|
|
// to the diagrams in "License Duration and Renewal", this tests a cyan bar
|
|
// with a green check mark.
|
|
// This does the work of the function above, except it allows the caller to
|
|
// explicitly set the timer_value_pointer. The timer_value_pointer can be a
|
|
// nullptr.
|
|
void RenewAndContinue(uint64_t start, uint64_t stop, uint64_t cutoff,
|
|
uint64_t renewal_duration_seconds,
|
|
uint64_t* timer_value_pointer) {
|
|
ASSERT_LT(start, stop);
|
|
if (cutoff > 0) ASSERT_LE(stop, cutoff);
|
|
// We'll fake instantaneous renewal requests. Flight time not important.
|
|
clock_values_.time_of_renewal_request = start;
|
|
const OEMCryptoResult result = ODK_ComputeRenewalDuration(
|
|
&timer_limits_, &clock_values_, start, renewal_duration_seconds,
|
|
timer_value_pointer);
|
|
// After first playback, the license is active.
|
|
EXPECT_EQ(clock_values_.status, kActive);
|
|
EXPECT_EQ(clock_values_.time_when_timer_expires, cutoff);
|
|
if (cutoff > 0) { // If we expect the timer to be set.
|
|
EXPECT_EQ(result, ODK_SET_TIMER);
|
|
if (timer_value_pointer != nullptr)
|
|
EXPECT_EQ(*timer_value_pointer, cutoff - start);
|
|
} else {
|
|
EXPECT_EQ(result, ODK_DISABLE_TIMER);
|
|
}
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(start, &timer_limits_, &clock_values_),
|
|
OEMCrypto_SUCCESS);
|
|
CheckClockValues(start);
|
|
const uint64_t mid = (start + stop) / 2;
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
|
|
OEMCrypto_SUCCESS);
|
|
CheckClockValues(mid);
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
|
|
OEMCrypto_SUCCESS);
|
|
CheckClockValues(stop);
|
|
}
|
|
|
|
// Verify that a renewal can be processed and attempt to play from |start| to
|
|
// after |cutoff|. Verify that we are allowed playback from |start| to
|
|
// |cutoff|, but playback not allowed after |cutoff|. If you refer to the
|
|
// diagrams in "License Duration and Renewal", this tests a cyan bar with a
|
|
// black X.
|
|
void RenewAndTerminate(uint64_t start, uint64_t cutoff,
|
|
uint64_t renewal_duration_seconds) {
|
|
RenewAndTerminate(start, cutoff, cutoff + 100, renewal_duration_seconds);
|
|
}
|
|
|
|
// Verify that a renewal can be processed and attempt to play from |start| to
|
|
// |stop|. Verify that we are allowed playback from |start| to |cutoff|, but
|
|
// playback not allowed after |cutoff|. If you refer to the diagrams in
|
|
// "License Duration and Renewal", this tests a cyan bar with a black X. This
|
|
// assumes that |cutoff| is between |start| and |stop|.
|
|
void RenewAndTerminate(uint64_t start, uint64_t cutoff, uint64_t stop,
|
|
uint64_t renewal_duration_seconds) {
|
|
ASSERT_LT(start, cutoff);
|
|
ASSERT_LT(cutoff, stop);
|
|
RenewAndContinue(start, cutoff, cutoff, renewal_duration_seconds);
|
|
EXPECT_EQ(
|
|
ODK_UpdateLastPlaybackTime(cutoff + 1, &timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
CheckClockValues(cutoff);
|
|
const uint64_t mid = (cutoff + stop) / 2;
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(mid, &timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
CheckClockValues(
|
|
cutoff); // times do not change if playback was not allowed.
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(stop, &timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
CheckClockValues(cutoff);
|
|
}
|
|
|
|
// Verify that a renewal can be processed and playback may start from
|
|
// |start| to |stop|. If |cutoff| is not 0, then expect the timer to expire
|
|
// at that time. If |cutoff| is 0, expect the timer is not set. If you refer
|
|
// to the diagrams in "License Duration and Renewal", this tests a cyan bar
|
|
// with a green check mark. This is different from the previous functions,
|
|
// because the renewal is loaded before the first playback.
|
|
void RenewAndStart(uint64_t start, uint64_t stop, uint64_t cutoff,
|
|
uint64_t renewal_duration_seconds) {
|
|
ASSERT_LT(start, stop);
|
|
if (cutoff > 0) ASSERT_LE(stop, cutoff);
|
|
// We'll fake instantaneous renewal requests. Flight time not important.
|
|
clock_values_.time_of_renewal_request = start;
|
|
uint64_t timer_value;
|
|
const OEMCryptoResult result =
|
|
ODK_ComputeRenewalDuration(&timer_limits_, &clock_values_, start,
|
|
renewal_duration_seconds, &timer_value);
|
|
EXPECT_EQ(clock_values_.time_when_timer_expires, cutoff);
|
|
if (cutoff > 0) { // If we expect the timer to be set.
|
|
EXPECT_EQ(result, ODK_SET_TIMER);
|
|
EXPECT_EQ(timer_value, cutoff - start);
|
|
} else {
|
|
EXPECT_EQ(result, ODK_DISABLE_TIMER);
|
|
}
|
|
AllowPlayback(start, stop, cutoff);
|
|
}
|
|
};
|
|
|
|
// License with Renewal, limited by playback duration. (See above for note on
|
|
// Use Case tests) These tests are parameterized. If the parameter is 0, we
|
|
// limit the playback duration. If the parameter is 1, we limit the rental
|
|
// duration. The behavior is basically the same.
|
|
class ODKUseCase_LicenseWithRenewal
|
|
: public RenewalTest,
|
|
public ::testing::WithParamInterface<int> {
|
|
public:
|
|
ODKUseCase_LicenseWithRenewal() {
|
|
// Either Playback or rental duration = 2 days hard
|
|
if (GetParam() == 0) {
|
|
timer_limits_.soft_enforce_rental_duration = false;
|
|
timer_limits_.rental_duration_seconds = 2000;
|
|
} else {
|
|
timer_limits_.soft_enforce_playback_duration = false;
|
|
timer_limits_.total_playback_duration_seconds = 2000;
|
|
}
|
|
timer_limits_.initial_renewal_duration_seconds = 50;
|
|
}
|
|
|
|
void SetUp() override {
|
|
RenewalTest::SetUp();
|
|
renewal_interval_ =
|
|
timer_limits_.initial_renewal_duration_seconds - kGracePeriod;
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
// Allow playback within the initial renewal window.
|
|
LoadAndAllowPlayback(start_of_playback_, next_renewal,
|
|
next_renewal + kGracePeriod);
|
|
}
|
|
|
|
uint64_t playback_end_restriction() {
|
|
if (GetParam() == 0) {
|
|
return EndOfRentalWindow();
|
|
} else {
|
|
return EndOfPlaybackWindow();
|
|
}
|
|
}
|
|
|
|
protected:
|
|
// How long to wait before we load the next renewal. i.e. cutoff -
|
|
// kGracePeriod. This is because the renewal_duration includes the grace
|
|
// period.
|
|
uint64_t renewal_interval_;
|
|
};
|
|
|
|
// Playback within rental duration and renewal duration.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case1) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
for (int i = 0; i < 4; i++) {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
}
|
|
|
|
// Playback within rental duration, last renewal_interval_ exceeds renewal
|
|
// duration.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case2) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
for (int i = 0; i < 4; i++) {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
// We attempt to continue playing beyond the renewal renewal_interval_.
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
|
|
// Playback interrupted by late renewal.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case3) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
for (int i = 0; i < 4; i++) {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
// We attempt to continue playing beyond the renewal renewal_interval_.
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
|
|
next_renewal + kGracePeriod, // stop: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
|
|
const uint64_t late_renewal = next_renewal + kGracePeriod + 10;
|
|
next_renewal = late_renewal + renewal_interval_;
|
|
RenewAndContinue(late_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
|
|
// Playback & restart within playback duration and renewal duration. Note: this
|
|
// simulates reloading a persistent license, and then reloading the last
|
|
// renewal.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case4) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
for (int i = 0; i < 2; i++) {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
// Reload license after timer expired.
|
|
uint64_t reload_time = next_renewal + kGracePeriod + 100;
|
|
next_renewal = reload_time + renewal_interval_;
|
|
ReloadLicense(reload_time);
|
|
RenewAndStart(reload_time, next_renewal, next_renewal + kGracePeriod,
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
for (int i = 0; i < 2; i++) {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
}
|
|
|
|
// Playback within renewal duration, but exceeds playback duration.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case5) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
do {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
} while ((next_renewal + renewal_interval_ + kGracePeriod) <
|
|
playback_end_restriction());
|
|
// Attempt playing beyond the playback window.
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
|
|
playback_end_restriction(), // stop: when timer expires.
|
|
timer_limits_.initial_renewal_duration_seconds);
|
|
}
|
|
|
|
// Renewal duration increases over time.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case6) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
do {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
renewal_interval_ + kGracePeriod);
|
|
// Renewal_Interval_ increases with each renewal.
|
|
renewal_interval_ += 100;
|
|
} while (next_renewal + renewal_interval_ + kGracePeriod <
|
|
playback_end_restriction());
|
|
// Attempt playing beyond the playback window:
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
|
|
playback_end_restriction(), // cutoff: when timer expires.
|
|
renewal_interval_ + kGracePeriod);
|
|
}
|
|
|
|
// Increasing renewal duration, playback exceeds last renewal.
|
|
// This is a mix between case 2 and case 6.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, Case7) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
for (int i = 0; i < 4; i++) {
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndContinue(
|
|
current_renewal, // start: when renewal is loaded.
|
|
next_renewal, // stop: expect play allowed.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
renewal_interval_ + kGracePeriod);
|
|
// Renewal_Interval_ increases with each renewal.
|
|
renewal_interval_ += 100;
|
|
}
|
|
const uint64_t current_renewal = next_renewal;
|
|
next_renewal = current_renewal + renewal_interval_;
|
|
RenewAndTerminate(current_renewal, // start: when renewal is loaded.
|
|
next_renewal + kGracePeriod, // cutoff: when timer expires.
|
|
renewal_interval_ + kGracePeriod);
|
|
}
|
|
|
|
// This is just like Case1, except we use a null pointer for the timer values in
|
|
// RenewAndContinue. It is not shown in the use case document.
|
|
TEST_P(ODKUseCase_LicenseWithRenewal, NullPointerTest) {
|
|
uint64_t next_renewal = start_of_playback_ + renewal_interval_;
|
|
const uint64_t start = next_renewal;
|
|
const uint64_t stop = start + renewal_interval_;
|
|
const uint64_t cutoff = stop + kGracePeriod;
|
|
const uint64_t renewal_duration_seconds =
|
|
timer_limits_.initial_renewal_duration_seconds;
|
|
uint64_t* timer_value_pointer = nullptr;
|
|
RenewAndContinue(start, stop, cutoff, renewal_duration_seconds,
|
|
timer_value_pointer);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(RestrictRenewal, ODKUseCase_LicenseWithRenewal,
|
|
::testing::Values(0, 1));
|
|
|
|
// Limited Duration License. (See above for notes on Use Case tests). The user
|
|
// has 15 minutes to begin watching the movie. If a renewal is not received,
|
|
// playback is terminated after 30 seconds. If a renewal is received, playback
|
|
// may continue for two hours from playback start.
|
|
class ODKUseCase_LimitedDurationLicense : public RenewalTest {
|
|
public:
|
|
ODKUseCase_LimitedDurationLicense() {
|
|
renewal_delay_ = 30u;
|
|
time_of_renewal_ = start_of_playback_ + renewal_delay_;
|
|
|
|
timer_limits_.soft_enforce_rental_duration = true;
|
|
timer_limits_.rental_duration_seconds = 150; // 15 minutes.
|
|
timer_limits_.soft_enforce_playback_duration = false;
|
|
timer_limits_.total_playback_duration_seconds = 2000; // Two hours.
|
|
timer_limits_.initial_renewal_duration_seconds =
|
|
renewal_delay_ + kGracePeriod;
|
|
}
|
|
uint64_t renewal_delay_;
|
|
uint64_t time_of_renewal_;
|
|
};
|
|
|
|
// Playback started within rental window and continues.
|
|
TEST_F(ODKUseCase_LimitedDurationLicense, Case1) {
|
|
// Allow playback within the initial renewal window.
|
|
LoadAndAllowPlayback(start_of_playback_, time_of_renewal_,
|
|
time_of_renewal_ + kGracePeriod);
|
|
const uint64_t renewal_duration = 2000; // two hour renewal.
|
|
const uint64_t play_for_one_hour = GetSystemTime(1000);
|
|
RenewAndContinue(time_of_renewal_, // start: when renewal is loaded.
|
|
play_for_one_hour, // stop: expect play allowed.
|
|
EndOfPlaybackWindow(), // cutoff: when timer expires.
|
|
renewal_duration);
|
|
}
|
|
|
|
// Playback started after rental duration.
|
|
TEST_F(ODKUseCase_LimitedDurationLicense, Case2) {
|
|
start_of_playback_ = EndOfRentalWindow() + 1;
|
|
ForbidPlayback(start_of_playback_);
|
|
}
|
|
|
|
// Playback started within rental window but renewal not received.
|
|
TEST_F(ODKUseCase_LimitedDurationLicense, Case3) {
|
|
// Allow playback within the initial renewal window.
|
|
LoadAndTerminatePlayback(start_of_playback_, time_of_renewal_ + kGracePeriod);
|
|
}
|
|
|
|
// Playback started within rental window, renewal is received, and playback
|
|
// continues.
|
|
TEST_F(ODKUseCase_LimitedDurationLicense, Case4) {
|
|
// Allow playback within the initial renewal window.
|
|
LoadAndAllowPlayback(start_of_playback_, time_of_renewal_,
|
|
time_of_renewal_ + kGracePeriod);
|
|
const uint64_t renewal_duration = 2000; // two hour renewal.
|
|
RenewAndTerminate(time_of_renewal_, // start: when renewal is loaded.
|
|
EndOfPlaybackWindow(), // stop: when timer expires.
|
|
renewal_duration);
|
|
}
|
|
|
|
// Playback may be restarted.
|
|
TEST_F(ODKUseCase_LimitedDurationLicense, Case5) {
|
|
// Allow playback within the initial renewal window.
|
|
LoadAndAllowPlayback(start_of_playback_, time_of_renewal_,
|
|
time_of_renewal_ + kGracePeriod);
|
|
const uint64_t renewal_duration = 2000; // two hour renewal.
|
|
const uint64_t play_for_one_hour = GetSystemTime(1000);
|
|
RenewAndContinue(time_of_renewal_, // start: when renewal is loaded.
|
|
play_for_one_hour, // stop: expect play allowed.
|
|
EndOfPlaybackWindow(), // cutoff: when timer expires.
|
|
renewal_duration);
|
|
|
|
const uint64_t reload_time = play_for_one_hour + 100;
|
|
ReloadLicense(reload_time);
|
|
// Simulate reloading the license, and then reloading the renewal, and then
|
|
// restarting playback. That is allowed, and playback shall be terminated at
|
|
// the end of the original playback window.
|
|
RenewAndStart(reload_time, EndOfPlaybackWindow(), EndOfPlaybackWindow(),
|
|
renewal_duration);
|
|
// But not one second more.
|
|
EXPECT_EQ(ODK_UpdateLastPlaybackTime(EndOfPlaybackWindow() + 1,
|
|
&timer_limits_, &clock_values_),
|
|
ODK_TIMER_EXPIRED);
|
|
CheckClockValues(EndOfPlaybackWindow());
|
|
}
|
|
|
|
// Verify that the backwards compatible function, ODK_InitializeV15Values, can
|
|
// set up the clock values and timer limits.
|
|
TEST_F(RenewalTest, V15Test) {
|
|
const uint32_t key_duration = 25;
|
|
ODK_NonceValues nonce_values;
|
|
memset(&nonce_values, 0, sizeof(nonce_values));
|
|
const uint64_t license_loaded = GetSystemTime(10);
|
|
EXPECT_EQ(
|
|
ODK_InitializeV15Values(&timer_limits_, &clock_values_, &nonce_values,
|
|
key_duration, license_loaded),
|
|
OEMCrypto_SUCCESS);
|
|
const uint64_t later_on = GetSystemTime(200);
|
|
TerminatePlayback(license_loaded, license_loaded + key_duration, later_on);
|
|
const uint32_t new_key_duration = 100;
|
|
RenewAndTerminate(later_on, later_on + new_key_duration, new_key_duration);
|
|
}
|
|
} // namespace
|