blob: 3552a39f61dbbe3bd69b7c98482ffdd73561443d [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "algorithms/forculus/forculus_analyzer.h"
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <chrono>
#include <ctime>
#include <fstream>
#include <streambuf>
#include "algorithms/forculus/forculus_encrypter.h"
#include "encoder/client_secret.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace cobalt {
namespace forculus {
using encoder::ClientSecret;
static const uint32_t kThreshold = 20;
namespace {
ForculusObservation Encrypt(const std::string& plaintext, double* wall_timer,
double* cpu_timer) {
// Make a config with the given threshold
ForculusConfig config;
config.set_threshold(kThreshold);
config.set_epoch_type(DAY);
// Construct an Encrypter.
ForculusEncrypter encrypter(config, 0, 0, 0, "",
ClientSecret::GenerateNewSecret());
// Invoke Encrypt() and check the status.
ForculusObservation obs;
auto c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();
EXPECT_EQ(ForculusEncrypter::kOK, encrypter.Encrypt(plaintext, 0, &obs));
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();
*wall_timer +=
std::chrono::duration<double, std::milli>(t_end - t_start).count() /
1000.0;
*cpu_timer += c_end - c_start;
return obs;
}
void AddObservations(ForculusAnalyzer* forculus_analyzer,
const std::string& plaintext, int num_clients,
double* encryption_wall_time, double* encryption_cpu_time,
double* decryption_wall_time,
double* decryption_cpu_time) {
// Simulate num_clients different clients.
for (int i = 0; i < num_clients; i++) {
auto observation =
Encrypt(plaintext, encryption_wall_time, encryption_cpu_time);
auto c_start = std::clock();
auto t_start = std::chrono::high_resolution_clock::now();
EXPECT_TRUE(forculus_analyzer->AddObservation(0, observation));
std::clock_t c_end = std::clock();
auto t_end = std::chrono::high_resolution_clock::now();
*decryption_wall_time +=
std::chrono::duration<double, std::milli>(t_end - t_start).count() /
1000.0;
*decryption_cpu_time += c_end - c_start;
}
}
} // namespace
// TODO(rudominer) The tests in this file find the text files to read using
// file paths that are expressed relative to the Cobalt source root directory.
// This technique works because when the tests are run via the Python script
// cobaltb.py then the current working directory is that root directory.
// This technique is fragile. We should instead pass the path to the root
// directory as a command-line argument into the test. Currently there is no
// Cobalt infrastructure for passing command-line arguments to unit tests.
// Reads the text file word_counts.txt containing words and counts. For each
// (word, count) pair constructs |count| independent Forculus Observations
// of |word|. Passes all of these Observations to a Forculus Analyzer and
// obtains the results. All together there will be one million Forculus
// Observations passed to the Forculus Analyzer. Prints out timing
// statistics at the end.
TEST(ForculusPerformanceTest, OneMillionObservations) {
ForculusConfig forculus_config;
forculus_config.set_threshold(kThreshold);
ForculusAnalyzer forculus_analyzer(forculus_config);
std::ifstream stream("algorithms/forculus/word_counts.txt",
std::ifstream::in);
std::string line;
double encryption_wall_time = 0;
double encryption_cpu_time = 0;
double decryption_wall_time = 0;
double decryption_cpu_time = 0;
int num_rows = 0;
while (std::getline(stream, line)) {
num_rows++;
std::istringstream line_stream(line);
std::string word;
line_stream >> word;
int count;
line_stream >> count;
AddObservations(&forculus_analyzer, word, count, &encryption_wall_time,
&encryption_cpu_time, &decryption_wall_time,
&decryption_cpu_time);
}
// The number of rows in the file word_counts.txt.
static const int kExpectedNumRows = 57792;
// There are one million observations
static const size_t kExpectedNumObservations = 1000000;
// The number of rows of word_counts.txt in which the count is at least 20.
static const size_t kExpectedNumResults = 5331;
EXPECT_EQ(kExpectedNumRows, num_rows);
EXPECT_EQ(kExpectedNumObservations, forculus_analyzer.num_observations());
auto results = forculus_analyzer.TakeResults();
EXPECT_EQ(kExpectedNumResults, results.size());
EXPECT_EQ(0u, forculus_analyzer.observation_errors());
std::cout << "\n=================================================\n";
std::cout << "Rows read: " << num_rows << std::endl;
std::cout << "Plaintexts encrypted: " << kExpectedNumObservations
<< std::endl;
std::cout << "Ciphertexts decrypted: " << results.size() << std::endl;
std::cout << "Total encryption wall time: " << encryption_wall_time
<< " seconds.\n";
std::cout << "Total encryption cpu time: "
<< encryption_cpu_time / CLOCKS_PER_SEC << " seconds.\n";
std::cout << "Total decryption wall time: " << decryption_wall_time
<< " seconds.\n";
std::cout << "Total decryption cpu time: "
<< decryption_cpu_time / CLOCKS_PER_SEC << " seconds.\n";
std::cout << "\n=================================================\n";
}
} // namespace forculus
} // namespace cobalt
int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}