| #include <gtest/gtest.h> |
| #include <stdexcept> |
| |
| #include "encoder.h" |
| #include "openssl_hash_impl.h" |
| #include "unix_kernel_rand_impl.h" |
| |
| // We need the same "random" inputs to the IRR |
| // each time to have reproducible tests. |
| FILE* mock_urandom(void) { |
| int i; |
| FILE *fp; |
| fp = tmpfile(); |
| for (i = 0; i < 1024; i++) { |
| fputc((i * 17) % 256, fp); |
| } |
| fflush(fp); |
| fp = freopen(NULL, "r", fp); |
| return fp; |
| } |
| |
| class EncoderTest : public ::testing::Test { |
| protected: |
| EncoderTest() { |
| encoder_id = std::string("metric-name").c_str(); |
| fp = mock_urandom(); |
| irr_rand = new rappor::UnixKernelRand(fp); |
| } |
| |
| virtual ~EncoderTest() { |
| fclose(fp); |
| delete irr_rand; |
| delete deps; |
| delete params; |
| delete encoder; |
| } |
| |
| FILE* fp; const char* encoder_id; |
| rappor::UnixKernelRand *irr_rand; |
| rappor::Deps *deps; |
| rappor::Params *params; |
| rappor::Encoder *encoder; |
| rappor::Bits bits_out; |
| std::vector<uint8_t> bits_vector; |
| }; |
| |
| // Uses HmacSha256 and 32-bit outputs. |
| class EncoderUint32Test : public EncoderTest { |
| protected: |
| EncoderUint32Test() { |
| deps = new rappor::Deps(rappor::Md5, "client-secret", rappor::HmacSha256, |
| *irr_rand); |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| encoder = new rappor::Encoder(encoder_id, *params, *deps); |
| } |
| }; |
| |
| // Uses HmacDrbg and variable-size vector outputs. |
| class EncoderUnlimTest : public EncoderTest { |
| protected: |
| EncoderUnlimTest() { |
| deps = new rappor::Deps(rappor::Md5, "client-secret", rappor::HmacDrbg, |
| *irr_rand); |
| params = new rappor::Params(64, // num_bits (k) |
| 2, // num_hashes (h) |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| encoder = new rappor::Encoder(encoder_id, *params, *deps); |
| } |
| }; |
| |
| |
| ///// EncoderUint32Test |
| TEST_F(EncoderUint32Test, EncodeStringUint32) { |
| ASSERT_TRUE(encoder->EncodeString("foo", &bits_out)); |
| ASSERT_EQ(2281639167, bits_out); |
| ASSERT_EQ(3, encoder->cohort()); |
| } |
| |
| TEST_F(EncoderUint32Test, EncodeStringUint32Cohort) { |
| encoder->set_cohort(4); // Set pre-selected cohort. |
| ASSERT_TRUE(encoder->EncodeString("foo", &bits_out)); |
| ASSERT_EQ(2281637247, bits_out); |
| ASSERT_EQ(4, encoder->cohort()); |
| } |
| |
| TEST_F(EncoderUint32Test, EncodeBitsUint32) { |
| ASSERT_TRUE(encoder->EncodeBits(0x123, &bits_out)); |
| ASSERT_EQ(2784956095, bits_out); |
| ASSERT_EQ(3, encoder->cohort()); |
| } |
| |
| // Negative tests |
| // num_bits is negative. |
| TEST_F(EncoderUint32Test, NumBitsMustBePositiveDeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| delete params; |
| params = new rappor::Params(-1, // num_bits (k) [BAD] |
| 2, // num_hashes (h) |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| } |
| |
| // num_hashes is negative. |
| TEST_F(EncoderUint32Test, NumHashesMustBePositiveDeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| -1, // num_hashes (h) [BAD] |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| } |
| |
| // num_cohorts is negative. |
| TEST_F(EncoderUint32Test, NumCohortsMustBePositiveDeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| -1, // num_cohorts (m) [BAD] |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Encoder.*Assertion.*failed"); |
| } |
| |
| // Invalid probabilities. |
| TEST_F(EncoderUint32Test, InvalidProbabilitiesDeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| // prob_f negative. |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 1, // num_cohorts (m) |
| -0.1, // probability f for PRR [BAD] |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| // prob_f > 1. |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 1, // num_cohorts (m) |
| 1.1, // probability f for PRR [BAD] |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| // prob_p < 0. |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 1, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| -0.1, // probability p for IRR [BAD] |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| // prob_p > 1. |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 1, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 1.1, // probability p for IRR [BAD] |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| // prob_q < 0. |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 1, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| -0.1); // probability q for IRR [BAD] |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| // prob_q > 1. |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 2, // num_hashes (h) |
| 1, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 1.1); // probability q for IRR [BAD] |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| } |
| |
| // num_bits 64 when only 32 bits are possible. |
| TEST_F(EncoderUint32Test, Sha256NoMoreThan32BitsDeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| delete params; |
| params = new rappor::Params(64, // num_bits (k) |
| 2, // num_hashes (h) |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| } |
| |
| // num_hashes too high. |
| TEST_F(EncoderUint32Test, NumHashesNoMoreThan16DeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| delete params; |
| params = new rappor::Params(32, // num_bits (k) |
| 17, // num_hashes (h) |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| } |
| |
| // EncoderString with 4-byte vector and HMACSHA256 and |
| // EncoderString with Uint32 and HMACSHA256 should match. |
| TEST_F(EncoderUint32Test, StringUint32AndStringVectorMatch) { |
| ASSERT_TRUE(encoder->EncodeString("foo", &bits_out)); |
| ASSERT_EQ(2281639167, bits_out); |
| std::vector<uint8_t> expected_out(4); |
| expected_out[0] = (bits_out & 0xFF000000) >> 24; |
| expected_out[1] = (bits_out & 0x00FF0000) >> 16; |
| expected_out[2] = (bits_out & 0x0000FF00) >> 8; |
| expected_out[3] = bits_out & 0x000000FF; |
| |
| // Reset the mock randomizer. |
| delete irr_rand; |
| delete deps; |
| delete encoder; |
| fclose(fp); |
| fp = mock_urandom(); |
| irr_rand = new rappor::UnixKernelRand(fp); |
| deps = new rappor::Deps(rappor::Md5, "client-secret", rappor::HmacSha256, |
| *irr_rand); |
| encoder = new rappor::Encoder(encoder_id, *params, *deps); |
| ASSERT_TRUE(encoder->EncodeString("foo", &bits_vector)); |
| ASSERT_EQ(expected_out, bits_vector); |
| } |
| |
| ///// EncoderUnlimTest |
| |
| TEST_F(EncoderUnlimTest, EncodeStringUint64) { |
| static const uint8_t ex[] = { 134, 255, 11, 255, 252, 119, 240, 223 }; |
| std::vector<uint8_t> expected_vector(ex, ex + sizeof(ex)); |
| |
| ASSERT_TRUE(encoder->EncodeString("foo", &bits_vector)); |
| ASSERT_EQ(expected_vector, bits_vector); |
| ASSERT_EQ(93, encoder->cohort()); |
| } |
| |
| // Negative tests. |
| TEST_F(EncoderUnlimTest, NumBitsNotMultipleOf8DeathTest) { |
| ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| delete params; |
| params = new rappor::Params(63, // num_bits (k) [BAD] |
| 17, // num_hashes (h) |
| 128, // num_cohorts (m) |
| 0.25, // probability f for PRR |
| 0.75, // probability p for IRR |
| 0.5); // probability q for IRR |
| EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps), |
| "Assertion.*failed"); |
| } |
| |
| int main(int argc, char **argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |