Skip to content

Commit e1bdd87

Browse files
committed
test logging updates and logging test updates
1 parent 6f55ddd commit e1bdd87

File tree

8 files changed

+215
-19
lines changed

8 files changed

+215
-19
lines changed

.github/actions/spelling/expect.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ Scm
493493
sddl
494494
secureobject
495495
securestring
496+
seekp
496497
seof
497498
servercert
498499
servercertificate
@@ -542,6 +543,7 @@ Tagit
542543
TARG
543544
taskhostw
544545
tcs
546+
tellp
545547
temppath
546548
testexampleinstaller
547549
thiscouldbeapc

src/AppInstallerCLITests/AppInstallerCLITests.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@
248248
<ClCompile Include="ExperimentalFeature.cpp" />
249249
<ClCompile Include="ExportFlow.cpp" />
250250
<ClCompile Include="FileCache.cpp" />
251+
<ClCompile Include="FileLogger.cpp" />
251252
<ClCompile Include="Filesystem.cpp" />
252253
<ClCompile Include="FolderFileWatcher.cpp" />
253254
<ClCompile Include="FontHelper.cpp" />

src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@
389389
<ClCompile Include="FontHelper.cpp">
390390
<Filter>Source Files\Repository</Filter>
391391
</ClCompile>
392+
<ClCompile Include="FileLogger.cpp">
393+
<Filter>Source Files\Common</Filter>
394+
</ClCompile>
392395
</ItemGroup>
393396
<ItemGroup>
394397
<None Include="PropertySheet.props" />
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#include "pch.h"
4+
#include "TestCommon.h"
5+
#include <AppInstallerFileLogger.h>
6+
#include <AppInstallerStrings.h>
7+
8+
using namespace AppInstaller::Logging;
9+
using namespace AppInstaller::Utility;
10+
using namespace TestCommon;
11+
12+
13+
std::string GetHeaderString()
14+
{
15+
return "TIME [CHAN] Header Message";
16+
}
17+
18+
std::string GetLargeString()
19+
{
20+
return R"([===|Clearly defined start to large string|===]
21+
While this string does not need to be particularly unique, it is still good if it is not easily duplicated by any other random set of data.
22+
It should also end in a character that is not used in any other way within these tests, so please don't include that character when writing tests.
23+
That character is &)";
24+
}
25+
26+
namespace
27+
{
28+
#define WINGET_DEFINE_STRING_ENUM(_enum_,_value_) constexpr std::string_view _enum_##_##_value_ = #_value_##sv
29+
30+
WINGET_DEFINE_STRING_ENUM(TagState, Unset);
31+
WINGET_DEFINE_STRING_ENUM(TagState, SetAtStart);
32+
WINGET_DEFINE_STRING_ENUM(TagState, SetAfterLogging);
33+
34+
WINGET_DEFINE_STRING_ENUM(MaximumSizeState, Zero);
35+
WINGET_DEFINE_STRING_ENUM(MaximumSizeState, SmallerThanLargeString);
36+
WINGET_DEFINE_STRING_ENUM(MaximumSizeState, EqualToLargeString);
37+
WINGET_DEFINE_STRING_ENUM(MaximumSizeState, SlightlyLargerThanLargeString);
38+
WINGET_DEFINE_STRING_ENUM(MaximumSizeState, MuchLargerThanLargeString);
39+
40+
constexpr std::string_view WrapIndicator = "--- log file has wrapped ---"sv;
41+
42+
constexpr size_t NewLineCharacterCount = 1;
43+
constexpr size_t SmallDifferenceSize = 10;
44+
constexpr AppInstaller::Logging::Channel DefaultChannel = AppInstaller::Logging::Channel::Core;
45+
constexpr AppInstaller::Logging::Level DefaultLevel = AppInstaller::Logging::Level::Info;
46+
47+
void ValidateFileContents(const std::filesystem::path& file, const std::vector<std::string_view>& expectedContents)
48+
{
49+
std::ifstream fileStream{ file };
50+
auto fileContents = ReadEntireStream(fileStream);
51+
std::string_view fileContentsView = fileContents;
52+
53+
std::string fileContentsCopy = fileContents;
54+
FindAndReplace(fileContentsCopy, "\r", "\\r");
55+
FindAndReplace(fileContentsCopy, "\n", "\\n");
56+
INFO("File contents:\n" << fileContentsCopy);
57+
58+
size_t currentPosition = 0;
59+
for (std::string_view expectedContent : expectedContents)
60+
{
61+
REQUIRE(currentPosition < fileContents.size());
62+
63+
if (expectedContent == WrapIndicator)
64+
{
65+
auto endLinePosition = fileContentsView.find('\n', currentPosition);
66+
REQUIRE(endLinePosition != -1);
67+
REQUIRE(endLinePosition >= expectedContent.size() + NewLineCharacterCount);
68+
auto actualContent = fileContentsView.substr(endLinePosition + 1 - expectedContent.size() - NewLineCharacterCount, expectedContent.size());
69+
REQUIRE(expectedContent == actualContent);
70+
currentPosition = endLinePosition + 1;
71+
}
72+
else
73+
{
74+
auto actualContent = fileContentsView.substr(currentPosition, expectedContent.size());
75+
REQUIRE(expectedContent == actualContent);
76+
currentPosition += expectedContent.size() + NewLineCharacterCount;
77+
}
78+
}
79+
}
80+
}
81+
82+
TEST_CASE("FileLogger_MaximumSize", "[logging]")
83+
{
84+
auto headerString = GetHeaderString();
85+
auto largeString = GetLargeString();
86+
auto tagState = GENERATE(TagState_Unset, TagState_SetAtStart, TagState_SetAfterLogging);
87+
auto sizeState = GENERATE(MaximumSizeState_Zero, MaximumSizeState_SmallerThanLargeString, MaximumSizeState_EqualToLargeString, MaximumSizeState_SlightlyLargerThanLargeString, MaximumSizeState_MuchLargerThanLargeString);
88+
89+
// Determine maximum size
90+
size_t maximumSize = 0;
91+
92+
if (sizeState == MaximumSizeState_SmallerThanLargeString)
93+
{
94+
maximumSize = largeString.size() - SmallDifferenceSize;
95+
}
96+
else if (sizeState == MaximumSizeState_EqualToLargeString)
97+
{
98+
maximumSize = largeString.size();
99+
}
100+
else if (sizeState == MaximumSizeState_SlightlyLargerThanLargeString)
101+
{
102+
maximumSize = largeString.size() + SmallDifferenceSize;
103+
}
104+
else if (sizeState == MaximumSizeState_MuchLargerThanLargeString)
105+
{
106+
maximumSize = largeString.size() * 2;
107+
}
108+
109+
INFO("Tag State: " << tagState << ", Size State: " << sizeState << "[" << maximumSize << "]");
110+
111+
TempFile tempFile{ "FileLogger_MaximumSize", ".log" };
112+
FileLogger logger{ tempFile };
113+
114+
INFO("File: " << tempFile.GetPath().u8string());
115+
116+
logger.SetMaximumSize(wil::safe_cast<std::ofstream::off_type>(maximumSize));
117+
118+
// Set tag and log strings
119+
size_t tagPosition = 0;
120+
if (tagState == TagState_SetAtStart)
121+
{
122+
logger.SetTag(Tag::HeadersComplete);
123+
}
124+
125+
logger.WriteDirect(DefaultChannel, DefaultLevel, headerString);
126+
127+
if (tagState == TagState_SetAfterLogging)
128+
{
129+
logger.SetTag(Tag::HeadersComplete);
130+
tagPosition = headerString.size() + NewLineCharacterCount;
131+
}
132+
133+
logger.WriteDirect(DefaultChannel, DefaultLevel, largeString);
134+
135+
size_t maximumAvailableSpace = std::numeric_limits<size_t>::max();
136+
if (maximumSize)
137+
{
138+
maximumAvailableSpace = maximumSize - tagPosition;
139+
}
140+
141+
// Calculate current state
142+
size_t currentAvailableSpace = maximumAvailableSpace - headerString.size() - NewLineCharacterCount;
143+
bool shouldWrap = largeString.size() > currentAvailableSpace;
144+
145+
INFO("Maximum Avilable: " << maximumAvailableSpace << ", Current Available: " << currentAvailableSpace << ", ShouldWrap: " << shouldWrap);
146+
147+
std::vector<std::string_view> expectedFileContents;
148+
149+
if (tagPosition || !shouldWrap)
150+
{
151+
expectedFileContents.push_back(headerString);
152+
}
153+
154+
if (shouldWrap)
155+
{
156+
expectedFileContents.push_back(WrapIndicator);
157+
}
158+
159+
std::string_view largeStringView = largeString;
160+
expectedFileContents.push_back(largeStringView.substr(0, std::min(largeString.size(), maximumAvailableSpace)));
161+
162+
ValidateFileContents(tempFile, expectedFileContents);
163+
164+
// Log again
165+
INFO("Second time logging large string");
166+
logger.WriteDirect(DefaultChannel, DefaultLevel, largeString);
167+
168+
// The maximum size is twice the large log, so anything with a limit will wrap
169+
shouldWrap = maximumSize != 0;
170+
171+
expectedFileContents.clear();
172+
173+
if (tagPosition || !shouldWrap)
174+
{
175+
expectedFileContents.push_back(headerString);
176+
}
177+
178+
if (shouldWrap)
179+
{
180+
expectedFileContents.push_back(WrapIndicator);
181+
}
182+
else
183+
{
184+
expectedFileContents.push_back(largeStringView);
185+
}
186+
187+
expectedFileContents.push_back(largeStringView.substr(0, std::min(largeString.size(), maximumAvailableSpace)));
188+
189+
ValidateFileContents(tempFile, expectedFileContents);
190+
}
191+
192+
// TODO: Test case where we make wrapping happen a very large number of times

src/AppInstallerCLITests/main.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,17 @@ int main(int argc, char** argv)
8282
TestCommon::TempFile::SetDestructorBehavior(TestCommon::TempFileDestructionBehavior::ShellExecuteOnFailure);
8383
}
8484
else if ("-log"s == argv[i])
85-
{
86-
Logging::FileLogger::Add();
85+
{
86+
auto logger = std::make_unique<Logging::FileLogger>();
87+
logger->SetMaximumSize(0);
88+
Logging::Log().AddLogger(std::move(logger));
8789
}
8890
else if ("-logto"s == argv[i])
8991
{
90-
++i;
91-
Logging::FileLogger::Add(std::filesystem::path{ argv[i] });
92+
++i;
93+
auto logger = std::make_unique<Logging::FileLogger>(std::filesystem::path{ argv[i] });
94+
logger->SetMaximumSize(0);
95+
Logging::Log().AddLogger(std::move(logger));
9296
}
9397
else if ("-tdd"s == argv[i])
9498
{

src/AppInstallerCLITests/pch.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
#include <wil/filesystem.h>
3333
#include <wil/resource.h>
34-
#include <wil/result_macros.h>
34+
#include <wil/result_macros.h>
35+
#include <wil/safecast.h>
3536
#include <wil/token_helpers.h>
3637

3738
#include <algorithm>

src/AppInstallerCommonCore/FileLogger.cpp

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,11 @@ namespace AppInstaller::Logging
6464
m_stream.close();
6565
}
6666

67-
void FileLogger::SetMaximumSize(std::ofstream::off_type maximumSize)
67+
FileLogger& FileLogger::SetMaximumSize(std::ofstream::off_type maximumSize)
6868
{
6969
THROW_HR_IF(E_INVALIDARG, maximumSize < 0);
7070
m_maximumSize = maximumSize;
71+
return *this;
7172
}
7273

7374
std::string FileLogger::GetNameForPath(const std::filesystem::path& filePath)
@@ -91,27 +92,19 @@ namespace AppInstaller::Logging
9192
return m_name;
9293
}
9394

94-
void FileLogger::Write(Channel channel, Level, std::string_view message) noexcept try
95+
void FileLogger::Write(Channel channel, Level level, std::string_view message) noexcept try
9596
{
9697
std::string log = ToLogLine(channel, message);
97-
std::string_view logView = log;
98-
HandleMaximumFileSize(logView);
99-
m_stream << logView << std::endl;
100-
}
101-
catch (...)
102-
{
103-
// Just eat any exceptions here; better than losing logs
98+
WriteDirect(channel, level, log);
10499
}
100+
catch (...) {}
105101

106102
void FileLogger::WriteDirect(Channel, Level, std::string_view message) noexcept try
107103
{
108104
HandleMaximumFileSize(message);
109105
m_stream << message << std::endl;
110106
}
111-
catch (...)
112-
{
113-
// Just eat any exceptions here; better than losing logs
114-
}
107+
catch (...) {}
115108

116109
void FileLogger::SetTag(Tag tag) noexcept try
117110
{

src/AppInstallerCommonCore/Public/AppInstallerFileLogger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace AppInstaller::Logging
2727

2828
// The default value for the maximum size comes from settings.
2929
// Setting the maximum size to 0 will disable the maximum.
30-
void SetMaximumSize(std::ofstream::off_type maximumSize);
30+
FileLogger& SetMaximumSize(std::ofstream::off_type maximumSize);
3131

3232
static std::string GetNameForPath(const std::filesystem::path& filePath);
3333

0 commit comments

Comments
 (0)