| // Copyright 2017 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. |
| #import "tools/observation_querier/observation_querier.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "analyzer/store/memory_store.h" |
| #include "analyzer/store/observation_store.h" |
| #include "gflags/gflags.h" |
| #include "glog/logging.h" |
| #include "third_party/googletest/googletest/include/gtest/gtest.h" |
| |
| namespace cobalt { |
| |
| using analyzer::store::DataStore; |
| using analyzer::store::MemoryStore; |
| using analyzer::store::ObservationStore; |
| |
| namespace { |
| |
| const uint32_t kCustomerId = 1; |
| const uint32_t kProjectId = 2; |
| const uint32_t kDayIndex = 3; |
| |
| } // namespace |
| |
| // Tests of ObservationQuerier. |
| class ObservationQuerierTest : public ::testing::Test { |
| public: |
| ObservationQuerierTest() |
| : data_store_(new MemoryStore()), |
| observation_store_(new ObservationStore(data_store_)), |
| querier_(kCustomerId, kProjectId, observation_store_, &output_stream_) { |
| } |
| |
| protected: |
| // Clears the contents of the ObservationQuerier's output stream and returns |
| // the contents prior to clearing. |
| std::string ClearOutput() { |
| std::string s = output_stream_.str(); |
| output_stream_.str(""); |
| return s; |
| } |
| |
| // Does the current contents of the ObservationQuerier's output stream contain |
| // the given text. |
| bool OutputContains(const std::string text) { |
| return std::string::npos != output_stream_.str().find(text); |
| } |
| |
| // Is the ObservationQuerier's output stream curently empty? |
| bool NoOutput() { return output_stream_.str().empty(); } |
| |
| // Writes an observation for the given |metric_id| into the ObservationStore. |
| // There will be |num_parts| parts named part0, part1, etc. Each part will |
| // have the same value given by |data|. If |forculus| is true all the |
| // parts will be Forculus observations otherwise all the parts will be |
| // Basic RAPPOR observations. |
| void WriteObservation(uint32_t metric_id, bool forculus, int num_parts, |
| std::string data) { |
| ObservationMetadata metadata; |
| metadata.set_customer_id(kCustomerId); |
| metadata.set_project_id(kProjectId); |
| metadata.set_metric_id(metric_id); |
| metadata.set_day_index(kDayIndex); |
| std::vector<Observation> observations; |
| observations.emplace_back(); |
| Observation& observation = observations.back(); |
| for (int i = 0; i < num_parts; i++) { |
| std::ostringstream stream; |
| stream << "part" << i; |
| std::string part_name = stream.str(); |
| ObservationPart& part = (*observation.mutable_parts())[part_name]; |
| if (forculus) { |
| part.mutable_forculus()->set_ciphertext(data); |
| } else { |
| part.mutable_basic_rappor()->set_data(data); |
| } |
| } |
| EXPECT_EQ(analyzer::store::kOK, |
| observation_store_->AddObservationBatch(metadata, observations)); |
| } |
| |
| // The output stream that the ObservationQuerier has been given. |
| std::ostringstream output_stream_; |
| |
| std::shared_ptr<DataStore> data_store_; |
| std::shared_ptr<ObservationStore> observation_store_; |
| |
| // The ObservationQuerier under test. |
| ObservationQuerier querier_; |
| }; |
| |
| // Tests processing a bad command line. |
| TEST_F(ObservationQuerierTest, ProcessCommandLineBad) { |
| EXPECT_TRUE(querier_.ProcessCommandLine("this is not a command")); |
| EXPECT_TRUE(OutputContains("Unrecognized command: this")); |
| } |
| |
| // Tests processing the "help" command |
| TEST_F(ObservationQuerierTest, ProcessCommandLineHelp) { |
| EXPECT_TRUE(querier_.ProcessCommandLine("help")); |
| // We don't want to test the actual output too rigorously because that would |
| // be a very fragile test. Just doing a sanity test. |
| EXPECT_TRUE(OutputContains("Print this help message.")); |
| EXPECT_TRUE(OutputContains("Query up to <max_num> observations.")); |
| } |
| |
| // Tests processing a bad set command line. |
| TEST_F(ObservationQuerierTest, ProcessCommandLineSetBad) { |
| EXPECT_TRUE(querier_.ProcessCommandLine("set")); |
| EXPECT_TRUE(OutputContains("Malformed set command.")); |
| ClearOutput(); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("set a b c")); |
| EXPECT_TRUE(OutputContains("Malformed set command.")); |
| ClearOutput(); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("set a b")); |
| EXPECT_TRUE(OutputContains("a is not a settable parameter")); |
| ClearOutput(); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("set metric b")); |
| EXPECT_TRUE(OutputContains("Expected positive integer instead of b.")); |
| ClearOutput(); |
| |
| ClearOutput(); |
| } |
| |
| // Tests processing the set and ls commands |
| TEST_F(ObservationQuerierTest, ProcessCommandLineSetAndLs) { |
| EXPECT_TRUE(querier_.ProcessCommandLine("ls")); |
| EXPECT_TRUE(OutputContains("Metric ID: 1")); |
| ClearOutput(); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("set metric 2")); |
| EXPECT_TRUE(NoOutput()); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("ls")); |
| EXPECT_TRUE(OutputContains("Metric ID: 2")); |
| } |
| |
| // Tests processing a bad query command |
| TEST_F(ObservationQuerierTest, ProcessCommandLineQueryBad) { |
| EXPECT_TRUE(querier_.ProcessCommandLine("query")); |
| EXPECT_TRUE( |
| OutputContains("Malformed query command. Expected query <max_num>")); |
| ClearOutput(); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("query a b")); |
| EXPECT_TRUE( |
| OutputContains("Malformed query command. Expected query <max_num>")); |
| ClearOutput(); |
| |
| EXPECT_TRUE(querier_.ProcessCommandLine("query a")); |
| EXPECT_TRUE(OutputContains("Expected positive integer instead of a.")); |
| ClearOutput(); |
| } |
| |
| // Tests processing the query command |
| TEST_F(ObservationQuerierTest, ProcessCommandLineQuery) { |
| // Query when there are no observations. |
| EXPECT_TRUE(querier_.ProcessCommandLine("query 3")); |
| EXPECT_TRUE(NoOutput()); |
| |
| // Add 3 Forculus observations for metric 1. |
| uint32_t metric_id = 1; |
| bool forculus = true; |
| WriteObservation(metric_id, forculus, 1, "hello"); |
| WriteObservation(metric_id, forculus, 1, "goodbye"); |
| WriteObservation(metric_id, forculus, 1, "peace"); |
| |
| // Add 2 basic RAPPOR observations for metric 2. |
| metric_id = 2; |
| forculus = false; |
| WriteObservation(metric_id, forculus, 1, "A"); |
| WriteObservation(metric_id, forculus, 1, "B"); |
| |
| // Query 3 observations from metric 1. |
| EXPECT_TRUE(querier_.ProcessCommandLine("query 3")); |
| EXPECT_TRUE( |
| OutputContains("part0:forculus:ciphertext:aGVsbG8=::point_x:")); |
| EXPECT_TRUE( |
| OutputContains("part0:forculus:ciphertext:Z29vZGJ5ZQ==::point_x:")); |
| EXPECT_TRUE( |
| OutputContains("part0:forculus:ciphertext:cGVhY2U=::point_x:")); |
| ClearOutput(); |
| |
| // Query 3 observations from metric 2. |
| querier_.ProcessCommandLine("set metric 2"); |
| EXPECT_TRUE(querier_.ProcessCommandLine("query 3")); |
| EXPECT_TRUE( |
| OutputContains("part0:basic_rappor:01000010")); |
| EXPECT_TRUE( |
| OutputContains("part0:basic_rappor:01000001")); |
| } |
| |
| // Tests processing the "quit" command |
| TEST_F(ObservationQuerierTest, ProcessCommandLineQuit) { |
| EXPECT_FALSE(querier_.ProcessCommandLine("quit")); |
| EXPECT_TRUE(NoOutput()); |
| } |
| |
| } // namespace cobalt |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| google::ParseCommandLineFlags(&argc, &argv, true); |
| google::InitGoogleLogging(argv[0]); |
| return RUN_ALL_TESTS(); |
| } |