// // tape_test.cpp // shared_random_generator-test // // Created by Sam Jaffe on 3/19/23. // Copyright © 2023 Sam Jaffe. All rights reserved. // #include "random/tape.h" #include #include #include "random/distribution.h" #include "random/random.h" #include "xcode_gtest_helper.h" using testing::ElementsAre; using testing::FieldsAre; template class Uniform : public engine::random::Uniform { public: Uniform(T min, T max) : engine::random::Uniform(min, max) {} T operator()(engine::random::Device &) const final { throw; } }; MATCHER_P(BytesAre, value, "") { *result_listener << "whose bytes are " << std::hex << arg; return reinterpret_cast(arg) == value; } TEST(TapeTest, CanRecordIntData) { engine::random::Tape tape; tape.apply(Uniform(0u, 100u), 50u); EXPECT_EQ(tape.apply(Uniform(0u, 100u)), 50); } TEST(TapeTest, CanRecordDoubleData) { engine::random::Tape tape; tape.apply(Uniform(0.0, 100.0), 50.0); EXPECT_EQ(tape.apply(Uniform(0.0, 100.0)), 50.0); tape.apply(Uniform(0.0, 100.0), 50.0); EXPECT_EQ(tape.apply(Uniform(0.0, 100.0)), 50.0); } TEST(TapeTest, ThrowsOnOverFetch) { engine::random::Tape tape; tape.apply(Uniform(0, 100), 50); EXPECT_EQ(tape.apply(Uniform(0, 100)), 50); EXPECT_THROW(tape.apply(Uniform(0, 100)), std::out_of_range); } TEST(TapeTest, BadRequestIsNoOp) { engine::random::Tape tape; tape.apply(Uniform(0, 100), 50); EXPECT_ANY_THROW(tape.apply(Uniform(0, 1))); EXPECT_EQ(tape.apply(Uniform(0, 100)), 50); } TEST(TapeTest, ThrowsOnTypeMismatch) { engine::random::Tape tape; tape.apply(Uniform(0u, 100u), 50u); EXPECT_THROW(tape.apply(Uniform(0.0, 100.0)), std::logic_error); EXPECT_THROW(tape.apply(Uniform(0, 100)), std::logic_error); EXPECT_NO_THROW(tape.apply(Uniform(0u, 100u))); } TEST(TapeTest, ThrowsOnBoundsMismatch) { engine::random::Tape tape; tape.apply(Uniform(0.0, 100.0), 50.0); EXPECT_THROW(tape.apply(Uniform(0.0, 1.0)), std::logic_error); } TEST(TapeTest, IsOrdered) { engine::random::Tape tape; tape.apply(Uniform(0.0, 100.0), 50.0); tape.apply(Uniform(10.0, 90.0), 50.0); EXPECT_THROW(tape.apply(Uniform(10.0, 90.0)), std::logic_error); EXPECT_EQ(tape.apply(Uniform(0.0, 100.0)), 50.0); EXPECT_EQ(tape.apply(Uniform(10.0, 90.0)), 50.0); } TEST(TapeTest, IsSerializable) { engine::random::Tape tape; tape.apply(Uniform(0, 100), 50); tape.apply(Uniform(0u, 100u), 50u); tape.apply(Uniform(0.0, 100.0), 50.0); auto serial = engine::random::Tape::serial_type(tape); EXPECT_THAT(serial, ElementsAre(FieldsAre("iUniform[0,100]", 50), FieldsAre("jUniform[0,100]", 50), FieldsAre("dUniform[0,100)", BytesAre(50)))); tape = engine::random::Tape(serial); EXPECT_EQ(tape.apply(Uniform(0, 100)), 50); EXPECT_EQ(tape.apply(Uniform(0u, 100u)), 50u); EXPECT_EQ(tape.apply(Uniform(0.0, 100.0)), 50.0); } TEST(TapeTest, IndexNotIncludedInSerialization) { engine::random::Tape tape; tape.apply(Uniform(0, 100), 50); tape.apply(Uniform(0u, 100u), 50u); tape.apply(Uniform(0.0, 100.0), 50.0); EXPECT_EQ(tape.apply(Uniform(0, 100)), 50); EXPECT_EQ(tape.apply(Uniform(0u, 100u)), 50u); auto serial = engine::random::Tape::serial_type(tape); EXPECT_THAT(serial, ElementsAre(FieldsAre("iUniform[0,100]", 50), FieldsAre("jUniform[0,100]", 50), FieldsAre("dUniform[0,100)", BytesAre(50)))); tape = engine::random::Tape(serial); EXPECT_EQ(tape.apply(Uniform(0, 100)), 50); EXPECT_EQ(tape.apply(Uniform(0u, 100u)), 50u); EXPECT_EQ(tape.apply(Uniform(0.0, 100.0)), 50.0); } TEST(TapeTest, CanAttachToRandom) { engine::random::Random random; auto tape = std::make_shared(); auto scope = random.record(tape); auto result = random(engine::random::Uniform(0, 100)); EXPECT_EQ(tape->apply(engine::random::Uniform(0, 100)), result); } TEST(TapeTest, StopsRecordingOnScopeExit) { engine::random::Random random; auto tape = std::make_shared(); { auto scope = random.record(tape); random(engine::random::Uniform(0, 100)); } random(engine::random::Uniform(0, 100)); EXPECT_NO_THROW(tape->apply(engine::random::Uniform(0, 100))); EXPECT_THROW(tape->apply(engine::random::Uniform(0, 100)), std::out_of_range); }