diff options
author | Mike Yu <yumike@google.com> | 2019-10-31 16:12:23 +0800 |
---|---|---|
committer | Mike Yu <yumike@google.com> | 2019-12-11 22:31:45 +0800 |
commit | 39df0b13bb000abfbc9fbb01edacd54f4fc421c9 (patch) | |
tree | 5c46857705be0841cc4b895cfa8e3994a2cae8f8 | |
parent | 939e3690aeb0142679678313a74bf024cfba7129 (diff) | |
download | DnsResolver-39df0b13bb000abfbc9fbb01edacd54f4fc421c9.tar.gz |
Add unit tests for DnsQueryLog
Ensure the basic functions of DnsQueryLog work as expected.
Bug: 139040977
Test: cd packages/modules/DnsResolver && atest
Change-Id: I6b318beeed7ff5942e8d08474c354e48ebdac936
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | DnsQueryLog.cpp | 6 | ||||
-rw-r--r-- | DnsQueryLog.h | 13 | ||||
-rw-r--r-- | DnsQueryLogTest.cpp | 164 |
4 files changed, 178 insertions, 6 deletions
@@ -171,6 +171,7 @@ cc_test { "resolv_tls_unit_test.cpp", "resolv_unit_test.cpp", "DnsStatsTest.cpp", + "DnsQueryLogTest.cpp", ], shared_libs: [ "libbase", diff --git a/DnsQueryLog.cpp b/DnsQueryLog.cpp index 82ed581d..6f0e1795 100644 --- a/DnsQueryLog.cpp +++ b/DnsQueryLog.cpp @@ -61,19 +61,19 @@ std::string timestampToString(const std::chrono::system_clock::time_point& ts) { void DnsQueryLog::push(Record&& record) { std::lock_guard guard(mLock); mQueue.push_back(std::move(record)); - if (mQueue.size() > kLogSize) { + if (mQueue.size() > mCapacity) { mQueue.pop_front(); } } void DnsQueryLog::dump(netdutils::DumpWriter& dw) const { - dw.println("DNS query log (last %ld minutes):", kValidityMinutes.count()); + dw.println("DNS query log (last %lld minutes):", (mValidityTimeMs / 60000).count()); netdutils::ScopedIndent indentStats(dw); const auto now = std::chrono::system_clock::now(); std::lock_guard guard(mLock); for (const auto& record : mQueue) { - if (now - record.timestamp > kValidityMinutes) continue; + if (now - record.timestamp > mValidityTimeMs) continue; const std::string maskedHostname = maskHostname(record.hostname); const std::string maskedIpsStr = maskIps(record.addrs); diff --git a/DnsQueryLog.h b/DnsQueryLog.h index a3190471..c19f8db3 100644 --- a/DnsQueryLog.h +++ b/DnsQueryLog.h @@ -49,18 +49,25 @@ class DnsQueryLog { const int timeTaken; }; + // Allow the tests to set the capacity and the validaty time in milliseconds. + DnsQueryLog(size_t size = kDefaultLogSize, + std::chrono::milliseconds time = kDefaultValidityMinutes) + : mCapacity(size), mValidityTimeMs(time) {} + void push(Record&& record) EXCLUDES(mLock); void dump(netdutils::DumpWriter& dw) const EXCLUDES(mLock); private: mutable std::mutex mLock; std::deque<Record> mQueue GUARDED_BY(mLock); + const size_t mCapacity; + const std::chrono::milliseconds mValidityTimeMs; // The capacity of the circular buffer. - static constexpr size_t kLogSize = 200; + static constexpr size_t kDefaultLogSize = 200; - // Limit to dump the queries within last |kValidityMinutes| minutes. - static constexpr std::chrono::minutes kValidityMinutes{60}; + // Limit to dump the queries within last |kDefaultValidityMinutes| minutes. + static constexpr std::chrono::minutes kDefaultValidityMinutes{60}; }; } // namespace android::net diff --git a/DnsQueryLogTest.cpp b/DnsQueryLogTest.cpp new file mode 100644 index 00000000..b652412a --- /dev/null +++ b/DnsQueryLogTest.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * 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 <regex> +#include <thread> + +#include <android-base/strings.h> +#include <android-base/test_utils.h> +#include <gtest/gtest.h> + +#include "DnsQueryLog.h" + +using namespace std::chrono_literals; + +namespace android::net { + +namespace { + +// Dump the log to STDOUT and capture it. +std::string captureDumpOutput(const DnsQueryLog& queryLog) { + netdutils::DumpWriter dw(STDOUT_FILENO); + CapturedStdout captured; + queryLog.dump(dw); + return captured.str(); +} + +// A simple check for the dump result by checking the netIds one by one. +void verifyDumpOutput(const std::string& dumpLog, const std::vector<int>& expectedNetIds) { + // Capture three matches: netId, hostname, and answer (empty allowed). + static const std::regex pattern( + R"(netId=(\d+).* hostname=([\w\*]+) answer=\[([\w:,\.\*\s]*)\])"); + + std::string str(dumpLog); + std::smatch sm; + for (const auto& netId : expectedNetIds) { + SCOPED_TRACE(netId); + EXPECT_TRUE(std::regex_search(str, sm, pattern)); + EXPECT_EQ(sm[1], std::to_string(netId)); + str = sm.suffix(); + } + + // Ensure the dumpLog is exactly as expected. + EXPECT_FALSE(std::regex_search(str, sm, pattern)); +} + +} // namespace + +class DnsQueryLogTest : public ::testing::Test { + protected: + const std::vector<std::string> serversV4 = {"127.0.0.1", "1.2.3.4"}; + const std::vector<std::string> serversV4V6 = {"127.0.0.1", "1.2.3.4", "2001:db8::1", + "fe80:1::2%testnet"}; +}; + +TEST_F(DnsQueryLogTest, Push) { + std::vector<DnsQueryLog::Record> records = { + DnsQueryLog::Record(30, 1000, 1000, "example.com", serversV4, 10), + DnsQueryLog::Record(31, 1000, 1000, "", serversV4, 10), // Empty hostname. + DnsQueryLog::Record(32, 1000, 1000, "example.com", {}, 10), // No answer. + DnsQueryLog::Record(33, 1000, 1000, "example.com", serversV4V6, 10), + }; + DnsQueryLog queryLog; + for (auto& r : records) { + queryLog.push(std::move(r)); + } + + std::string output = captureDumpOutput(queryLog); + verifyDumpOutput(output, {30, 31, 32, 33}); +} + +TEST_F(DnsQueryLogTest, PushStressTest) { + const int threadNum = 100; + const int pushNum = 1000; + const size_t size = 500; + DnsQueryLog queryLog(size); + std::vector<std::thread> threads(threadNum); + + // Launch 'threadNum' threads to push the same queryLog 'pushNum' times. + for (auto& thread : threads) { + thread = std::thread([&]() { + for (int i = 0; i < pushNum; i++) { + DnsQueryLog::Record record(30, 1000, 1000, "www.example.com", serversV4, 10); + queryLog.push(std::move(record)); + } + }); + } + for (auto& thread : threads) { + thread.join(); + } + + // Verify there are exact 'size' records in queryLog. + std::string output = captureDumpOutput(queryLog); + verifyDumpOutput(output, std::vector(size, 30)); +} + +TEST_F(DnsQueryLogTest, ZeroSize) { + const size_t size = 0; + DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10); + DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10); + DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10); + + DnsQueryLog queryLog(size); + queryLog.push(std::move(r1)); + queryLog.push(std::move(r2)); + queryLog.push(std::move(r3)); + + std::string output = captureDumpOutput(queryLog); + verifyDumpOutput(output, {}); +} + +TEST_F(DnsQueryLogTest, CapacityFull) { + const size_t size = 3; + DnsQueryLog::Record r1(30, 1000, 1000, "www.example1.com", serversV4V6, 10); + DnsQueryLog::Record r2(31, 1000, 1000, "www.example2.com", serversV4V6, 10); + DnsQueryLog::Record r3(32, 1000, 1000, "www.example3.com", serversV4V6, 10); + DnsQueryLog::Record r4(33, 1000, 1000, "www.example4.com", serversV4V6, 10); + const std::vector<int> expectedNetIds = {31, 32, 33}; + + DnsQueryLog queryLog(size); + queryLog.push(std::move(r1)); + queryLog.push(std::move(r2)); + queryLog.push(std::move(r3)); + queryLog.push(std::move(r4)); + + std::string output = captureDumpOutput(queryLog); + verifyDumpOutput(output, expectedNetIds); +} + +TEST_F(DnsQueryLogTest, ValidityTime) { + DnsQueryLog::Record r1(30, 1000, 1000, "www.example.com", serversV4, 10); + DnsQueryLog queryLog(3, 100ms); + queryLog.push(std::move(r1)); + + // Dump the output and verify the correctness by checking netId. + std::string output = captureDumpOutput(queryLog); + verifyDumpOutput(output, {30}); + + std::this_thread::sleep_for(150ms); + + // The record is expired thus not shown in the output. + output = captureDumpOutput(queryLog); + verifyDumpOutput(output, {}); + + // Push another record to ensure it still works. + DnsQueryLog::Record r2(31, 1000, 1000, "example.com", serversV4V6, 10); + queryLog.push(std::move(r2)); + output = captureDumpOutput(queryLog); + verifyDumpOutput(output, {31}); +} + +} // namespace android::net |