| /* |
| * Copyright (C) 2016, 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 <array> |
| #include <iostream> |
| #include <memory> |
| #include <tuple> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| #include "wifilogd/local_utils.h" |
| #include "wifilogd/os.h" |
| #include "wifilogd/tests/mock_raw_os.h" |
| |
| // This function must be defined in the same namespace as |timespec|. Hence the |
| // placement of this function at the top level. |
| inline void PrintTo(const timespec& ts, ::std::ostream* os) { |
| *os << "[secs:" << ts.tv_sec << " " |
| << "nsecs:" << ts.tv_nsec << "]"; |
| } |
| |
| namespace android { |
| namespace wifilogd { |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::InSequence; |
| using ::testing::Matcher; |
| using ::testing::MatcherInterface; |
| using ::testing::MatchResultListener; |
| using ::testing::NotNull; |
| using ::testing::Pointee; |
| using ::testing::Return; |
| using ::testing::SetArgumentPointee; |
| using ::testing::SetErrnoAndReturn; |
| using ::testing::StrictMock; |
| using ::testing::StrEq; |
| |
| using local_utils::GetMaxVal; |
| |
| class OsTest : public ::testing::Test { |
| public: |
| OsTest() { |
| raw_os_ = new StrictMock<MockRawOs>(); |
| os_ = std::unique_ptr<Os>(new Os(std::unique_ptr<RawOs>(raw_os_))); |
| } |
| |
| protected: |
| std::unique_ptr<Os> os_; |
| // We use a raw pointer to access the mock, since ownership passes |
| // to |os_|. |
| MockRawOs* raw_os_; |
| }; |
| |
| class TimespecMatcher : public MatcherInterface<const timespec&> { |
| public: |
| explicit TimespecMatcher(const timespec& expected) : expected_(expected) {} |
| |
| virtual void DescribeTo(::std::ostream* os) const { |
| *os << "equals "; |
| PrintTo(expected_, os); |
| } |
| |
| virtual bool MatchAndExplain(const timespec& actual, |
| MatchResultListener* /* listener */) const { |
| return actual.tv_sec == expected_.tv_sec && |
| actual.tv_nsec == expected_.tv_nsec; |
| } |
| |
| private: |
| const timespec& expected_; |
| }; |
| |
| Matcher<const timespec&> EqualsTimespec(const timespec& expected) { |
| return MakeMatcher(new TimespecMatcher(expected)); |
| } |
| |
| } // namespace |
| |
| TEST_F(OsTest, GetControlSocketReturnsFdAndZeroOnSuccess) { |
| constexpr char kSocketName[] = "fake-daemon"; |
| constexpr int kFakeValidFd = 100; |
| EXPECT_CALL(*raw_os_, GetControlSocket(StrEq(kSocketName))) |
| .WillOnce(Return(kFakeValidFd)); |
| |
| constexpr std::tuple<int, Os::Errno> kExpectedResult{kFakeValidFd, 0}; |
| EXPECT_EQ(kExpectedResult, os_->GetControlSocket(kSocketName)); |
| } |
| |
| TEST_F(OsTest, GetControlSocketReturnsInvalidFdAndErrorOnFailure) { |
| constexpr char kSocketName[] = "fake-daemon"; |
| constexpr Os::Errno kError = EINVAL; |
| EXPECT_CALL(*raw_os_, GetControlSocket(StrEq(kSocketName))) |
| .WillOnce(SetErrnoAndReturn(kError, -1)); |
| |
| constexpr std::tuple<int, Os::Errno> kExpectedResult{Os::kInvalidFd, kError}; |
| EXPECT_EQ(kExpectedResult, os_->GetControlSocket(kSocketName)); |
| } |
| |
| TEST_F(OsTest, GetTimestampSucceeds) { |
| constexpr auto kFakeSecs = 1U; |
| constexpr auto kFakeNsecs = 2U; |
| constexpr struct timespec fake_time { kFakeSecs, kFakeNsecs }; |
| EXPECT_CALL(*raw_os_, ClockGettime(_, _)) |
| .WillOnce(DoAll(SetArgumentPointee<1>(fake_time), Return(0))); |
| |
| const Os::Timestamp received = os_->GetTimestamp(CLOCK_REALTIME); |
| EXPECT_EQ(kFakeSecs, received.secs); |
| EXPECT_EQ(kFakeNsecs, received.nsecs); |
| } |
| |
| TEST_F(OsTest, NanosleepPassesNormalValueToSyscall) { |
| constexpr auto kSleepTimeNsec = 100; |
| EXPECT_CALL(*raw_os_, |
| Nanosleep(Pointee(EqualsTimespec({0, kSleepTimeNsec})), _)); |
| os_->Nanosleep(kSleepTimeNsec); |
| } |
| |
| TEST_F(OsTest, NanosleepPassesMaxmimalValueToSyscall) { |
| EXPECT_CALL(*raw_os_, |
| Nanosleep(Pointee(EqualsTimespec({0, Os::kMaxNanos})), _)); |
| os_->Nanosleep(Os::kMaxNanos); |
| } |
| |
| TEST_F(OsTest, NanosleepPassesZeroValueToSyscall) { |
| EXPECT_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, 0})), _)); |
| os_->Nanosleep(0); |
| } |
| |
| TEST_F(OsTest, NanosleepClampsOverlyLargeValue) { |
| EXPECT_CALL(*raw_os_, |
| Nanosleep(Pointee(EqualsTimespec({0, Os::kMaxNanos})), _)); |
| os_->Nanosleep(Os::kMaxNanos + 1); |
| } |
| |
| TEST_F(OsTest, NanosleepRetriesOnInterruptedCall) { |
| InSequence seq; |
| EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull())) |
| .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) { |
| *remaining = {0, 100}; |
| errno = EINTR; |
| return -1; |
| })); |
| EXPECT_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, 100})), _)); |
| os_->Nanosleep(Os::kMaxNanos); |
| } |
| |
| TEST_F(OsTest, NanosleepRetriesMultipleTimesIfNecessary) { |
| InSequence seq; |
| EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull())) |
| .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) { |
| *remaining = {0, 100}; |
| errno = EINTR; |
| return -1; |
| })); |
| EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull())) |
| .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) { |
| *remaining = {0, 50}; |
| errno = EINTR; |
| return -1; |
| })); |
| EXPECT_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, 50})), _)); |
| os_->Nanosleep(Os::kMaxNanos); |
| } |
| |
| TEST_F(OsTest, NanosleepIgnoresEintrWithZeroTimeRemaining) { |
| InSequence seq; |
| EXPECT_CALL(*raw_os_, Nanosleep(_, NotNull())) |
| .WillOnce(Invoke([](const timespec* /* desired */, timespec* remaining) { |
| *remaining = {0, 0}; |
| errno = EINTR; |
| return -1; |
| })); |
| EXPECT_CALL(*raw_os_, Nanosleep(_, _)).Times(0); |
| os_->Nanosleep(Os::kMaxNanos); |
| } |
| |
| TEST_F(OsTest, ReceiveDatagramReturnsCorrectValueForMaxSizedDatagram) { |
| constexpr int kFakeFd = 100; |
| std::array<uint8_t, 8192> buffer{}; |
| EXPECT_CALL(*raw_os_, Recv(kFakeFd, buffer.data(), buffer.size(), MSG_TRUNC)) |
| .WillOnce(Return(buffer.size())); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{buffer.size(), 0}; |
| EXPECT_EQ(kExpectedResult, |
| os_->ReceiveDatagram(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, ReceieveDatagramReturnsCorrectValueForRegularSizedDatagram) { |
| constexpr int kFakeFd = 100; |
| constexpr auto kReadBufferSize = 8192; |
| constexpr auto kDatagramSize = kReadBufferSize / 2; |
| std::array<uint8_t, kReadBufferSize> buffer{}; |
| EXPECT_CALL(*raw_os_, Recv(kFakeFd, buffer.data(), buffer.size(), MSG_TRUNC)) |
| .WillOnce(Return(kDatagramSize)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{kDatagramSize, 0}; |
| EXPECT_EQ(kExpectedResult, |
| os_->ReceiveDatagram(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, ReceieveDatagramReturnsCorrectValueForOversizedDatagram) { |
| constexpr int kFakeFd = 100; |
| constexpr auto kReadBufferSize = 8192; |
| constexpr auto kDatagramSize = kReadBufferSize * 2; |
| std::array<uint8_t, kReadBufferSize> buffer{}; |
| EXPECT_CALL(*raw_os_, Recv(kFakeFd, buffer.data(), buffer.size(), MSG_TRUNC)) |
| .WillOnce(Return(kDatagramSize)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{kDatagramSize, 0}; |
| EXPECT_EQ(kExpectedResult, |
| os_->ReceiveDatagram(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, ReceieveDatagramReturnsCorrectValueForZeroByteDatagram) { |
| constexpr int kFakeFd = 100; |
| std::array<uint8_t, 8192> buffer{}; |
| EXPECT_CALL(*raw_os_, Recv(kFakeFd, buffer.data(), buffer.size(), MSG_TRUNC)) |
| .WillOnce(Return(0)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{0, 0}; |
| EXPECT_EQ(kExpectedResult, |
| os_->ReceiveDatagram(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, ReceieveDatagramReturnsCorrectValueOnFailure) { |
| constexpr int kFakeFd = 100; |
| constexpr Os::Errno kError = EBADF; |
| std::array<uint8_t, 8192> buffer{}; |
| EXPECT_CALL(*raw_os_, Recv(kFakeFd, buffer.data(), buffer.size(), MSG_TRUNC)) |
| .WillOnce(SetErrnoAndReturn(kError, -1)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{0, kError}; |
| EXPECT_EQ(kExpectedResult, |
| os_->ReceiveDatagram(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, WriteReturnsCorrectValueForSuccessfulWrite) { |
| constexpr int kFakeFd = 100; |
| constexpr std::array<uint8_t, 8192> buffer{}; |
| EXPECT_CALL(*raw_os_, Write(kFakeFd, buffer.data(), buffer.size())) |
| .WillOnce(Return(buffer.size())); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{buffer.size(), 0}; |
| EXPECT_EQ(kExpectedResult, os_->Write(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, WriteReturnsCorrectValueForTruncatedWrite) { |
| constexpr int kFakeFd = 100; |
| constexpr int kBytesWritten = 4096; |
| constexpr std::array<uint8_t, 8192> buffer{}; |
| EXPECT_CALL(*raw_os_, Write(kFakeFd, buffer.data(), buffer.size())) |
| .WillOnce(Return(kBytesWritten)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{kBytesWritten, 0}; |
| EXPECT_EQ(kExpectedResult, os_->Write(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, WriteReturnsCorrectValueForSuccessfulZeroByteWrite) { |
| constexpr int kFakeFd = 100; |
| constexpr std::array<uint8_t, 0> buffer{}; |
| EXPECT_CALL(*raw_os_, Write(kFakeFd, buffer.data(), 0)).WillOnce(Return(0)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{0, 0}; |
| EXPECT_EQ(kExpectedResult, os_->Write(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, WriteReturnsCorrectValueForFailedWrite) { |
| constexpr int kFakeFd = 100; |
| constexpr Os::Errno kError = EBADF; |
| constexpr std::array<uint8_t, 8192> buffer{}; |
| EXPECT_CALL(*raw_os_, Write(kFakeFd, buffer.data(), buffer.size())) |
| .WillOnce(SetErrnoAndReturn(kError, -1)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{0, kError}; |
| EXPECT_EQ(kExpectedResult, os_->Write(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| TEST_F(OsTest, WriteReturnsCorrectValueForFailedZeroByteWrite) { |
| constexpr int kFakeFd = 100; |
| constexpr Os::Errno kError = EBADF; |
| constexpr std::array<uint8_t, 0> buffer{}; |
| EXPECT_CALL(*raw_os_, Write(kFakeFd, buffer.data(), 0)) |
| .WillOnce(SetErrnoAndReturn(kError, -1)); |
| |
| constexpr std::tuple<size_t, Os::Errno> kExpectedResult{0, kError}; |
| EXPECT_EQ(kExpectedResult, os_->Write(kFakeFd, buffer.data(), buffer.size())); |
| } |
| |
| // Per |
| // github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests, |
| // death tests should be specially named. |
| using OsDeathTest = OsTest; |
| |
| TEST_F(OsDeathTest, GetTimestampOverlyLargeNsecsCausesDeath) { |
| constexpr auto kFakeSecs = 1U; |
| constexpr auto kFakeNsecs = 1000 * 1000 * 1000; |
| constexpr struct timespec fake_time { kFakeSecs, kFakeNsecs }; |
| ON_CALL(*raw_os_, ClockGettime(_, _)) |
| .WillByDefault(DoAll(SetArgumentPointee<1>(fake_time), Return(0))); |
| EXPECT_DEATH(os_->GetTimestamp(CLOCK_REALTIME), "Check failed"); |
| } |
| |
| TEST_F(OsDeathTest, GetTimestampRawOsErrorCausesDeath) { |
| ON_CALL(*raw_os_, ClockGettime(_, _)).WillByDefault(Return(-1)); |
| EXPECT_DEATH(os_->GetTimestamp(CLOCK_REALTIME), "Unexpected error"); |
| } |
| |
| TEST_F(OsDeathTest, NanosleepUnexpectedErrorCausesDeath) { |
| ON_CALL(*raw_os_, Nanosleep(Pointee(EqualsTimespec({0, Os::kMaxNanos})), _)) |
| .WillByDefault(SetErrnoAndReturn(EFAULT, -1)); |
| EXPECT_DEATH(os_->Nanosleep(Os::kMaxNanos), "Unexpected error"); |
| } |
| |
| TEST_F(OsDeathTest, ReceiveDatagramWithOverlyLargeBufferCausesDeath) { |
| constexpr int kFakeFd = 100; |
| std::array<uint8_t, 8192> buffer{}; |
| EXPECT_DEATH( |
| os_->ReceiveDatagram(kFakeFd, buffer.data(), GetMaxVal<size_t>()), |
| "Check failed"); |
| } |
| |
| TEST_F(OsDeathTest, WriteWithOverlyLargeBufferCausesDeath) { |
| constexpr int kFakeFd = 100; |
| constexpr std::array<uint8_t, 8192> buffer{}; |
| EXPECT_DEATH(os_->Write(kFakeFd, buffer.data(), GetMaxVal<size_t>()), |
| "Check failed"); |
| } |
| |
| TEST_F(OsDeathTest, WriteWithOverrunCausesDeath) { |
| constexpr int kFakeFd = 100; |
| constexpr std::array<uint8_t, 8192> buffer{}; |
| ON_CALL(*raw_os_, Write(kFakeFd, buffer.data(), buffer.size())) |
| .WillByDefault(Return(buffer.size() + 1)); |
| EXPECT_DEATH(os_->Write(kFakeFd, buffer.data(), buffer.size()), |
| "Check failed"); |
| } |
| |
| } // namespace wifilogd |
| } // namespace android |