diff options
Diffstat (limited to 'client/cpp/encoder_unittest.cc')
-rw-r--r-- | client/cpp/encoder_unittest.cc | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/client/cpp/encoder_unittest.cc b/client/cpp/encoder_unittest.cc new file mode 100644 index 0000000..0f48604 --- /dev/null +++ b/client/cpp/encoder_unittest.cc @@ -0,0 +1,289 @@ +#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(); +} |