/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "StdAfx.h" #include #include #include #include #include // for max path decl #include #include #include #include namespace CLogUnitTests { using ::testing::NiceMock; using ::testing::_; using ::testing::Return; // for fuzzing test, how much work to do? Not much, as this must be fast. const int NumTrialsToPerform = 16000; class CLogUnitTests : public ::testing::Test , public UnitTest::TraceBusRedirector { public: using CryPrimitivesAllocatorScope = AZ::AllocatorScope; void SetUp() override { m_primitiveAllocators.ActivateAllocators(); m_priorEnv = gEnv; m_priorFileIO = AZ::IO::FileIOBase::GetInstance(); m_data = AZStd::make_unique(); m_data->m_stubEnv.pSystem = &m_data->m_system; gEnv = &m_data->m_stubEnv; // for FileIO, you must set the instance to null before changing it. // this is a way to tell the singleton system that you mean to replace a singleton and its // not a mistake. AZ::IO::FileIOBase::SetInstance(nullptr); AZ::IO::FileIOBase::SetInstance(&m_data->m_fileIOMock); ON_CALL(m_data->m_system, GetIRemoteConsole()) .WillByDefault( Return(&m_data->m_remoteConsoleMock)); AZ::IO::MockFileIOBase::InstallDefaultReturns(m_data->m_fileIOMock); BusConnect(); } void TearDown() override { BusDisconnect(); AZ::IO::FileIOBase::SetInstance(nullptr); AZ::IO::FileIOBase::SetInstance(m_priorFileIO); m_data.reset(); // restore state. gEnv = m_priorEnv; m_primitiveAllocators.DeactivateAllocators(); } struct DataMembers { SSystemGlobalEnvironment m_stubEnv; NiceMock m_system; NiceMock m_fileIOMock; NiceMock m_remoteConsoleMock; }; AZStd::unique_ptr m_data; SSystemGlobalEnvironment* m_priorEnv = nullptr; ISystem* m_priorSystem = nullptr; AZ::IO::FileIOBase* m_priorFileIO = nullptr; CryPrimitivesAllocatorScope m_primitiveAllocators; }; TEST_F(CLogUnitTests, LogAlways_InvalidString_Asserts) { AZ_TEST_START_TRACE_SUPPRESSION; CLog testLog(&m_data->m_system); testLog.LogAlways(nullptr); AZ_TEST_STOP_TRACE_SUPPRESSION(1); } TEST_F(CLogUnitTests, LogAlways_EmptyString_IgnoresWithoutCrashing) { CLog testLog(&m_data->m_system); testLog.LogAlways(""); } TEST_F(CLogUnitTests, LogAlways_NormalString_NoFileName_DoesNotCrash) { CLog testLog(&m_data->m_system); testLog.LogAlways("test"); } TEST_F(CLogUnitTests, LogAlways_SetFileName_Empty_DoesNotCrash) { CLog testLog(&m_data->m_system); testLog.SetFileName("", false); testLog.LogAlways("test"); } TEST_F(CLogUnitTests, LogAlways_FuzzTest) { CLog testLog(&m_data->m_system); AZStd::string randomJunkName; randomJunkName.resize(128, '\0'); // expect the mock to repeatedly get called. If we fail this expectation // it means the code is early-outing somewhere and we are not getting coverage. EXPECT_CALL(m_data->m_fileIOMock, Write(_, _, _, _)) .WillRepeatedly( Return(AZ::IO::Result(AZ::IO::ResultCode::Success))); // don't rely on randomness in unit tests, they need to be repeatable. // the following random generator is not seeded by the time, but by a constant (default 1234). AZ::SimpleLcgRandom randGen; for (int trialNumber = 0; trialNumber < NumTrialsToPerform; ++trialNumber) { for (int randomChar = 0; randomChar < randomJunkName.size(); ++randomChar) { // note that this is intentionally allowing null characters to generate. // note that this also puts characters AFTER the null, if a null appears in the mddle. // so that if there are off by one errors they could include cruft afterwards. if (randomChar > trialNumber % randomJunkName.size()) { // choose this point for the nulls to begin. It makes sure we test every size of string. randomJunkName[randomChar] = 0; } else { randomJunkName[randomChar] = (char)(randGen.GetRandom() % 256); // this will trigger invalid UTF8 decoding too } } testLog.LogAlways("%s", randomJunkName.c_str()); } } TEST_F(CLogUnitTests, LogAlways_SetFileName_Correct_DoesNotCrash_WritesToFile) { CLog testLog(&m_data->m_system); testLog.SetFileName("logfile.log", false); // EXPECT a call to the file system - if we dont get a call here, it means something went wrong. // it also expects exactly one call to write. One call to log should be one call to write, // or else performance will suffer. EXPECT_CALL(m_data->m_fileIOMock, Write(_, _, _, _)) .WillOnce( Return(AZ::IO::Result(AZ::IO::ResultCode::Success))); testLog.LogAlways("test"); } } // end namespace CLogUnitTests