Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/AppInstallerCLICore/ChannelStreams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ namespace AppInstaller::CLI::Execution
return *this;
}

OutputStream& OutputStream::operator<<(const std::filesystem::path& path)
{
ApplyFormat();
m_out << path.u8string();
return *this;
}

NoVTStream::NoVTStream(std::ostream& out, bool enabled) :
m_out(out, enabled, false) {}

Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLICore/ChannelStreams.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ namespace AppInstaller::CLI::Execution
OutputStream& operator<<(std::ostream& (__cdecl* f)(std::ostream&));
OutputStream& operator<<(const VirtualTerminal::Sequence& sequence);
OutputStream& operator<<(const VirtualTerminal::ConstructedSequence& sequence);
OutputStream& operator<<(const std::filesystem::path& path);

private:
// Applies the format for the stream.
Expand Down
16 changes: 8 additions & 8 deletions src/AppInstallerCLIE2ETests/SourceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void SourceAddWithDuplicateName()
public void SourceAddWithInvalidURL()
{
// Add source with invalid url should fail
var result = TestCommon.RunAICLICommand("source add", "AnotherSource https://microsoft.com");
var result = TestCommon.RunAICLICommand("source add", $"AnotherSource {Constants.TestSourceUrl}/Invalid/Directory/Dont/Add/Me");
Assert.AreEqual(Constants.ErrorCode.HTTP_E_STATUS_NOT_FOUND, result.ExitCode);
Assert.True(result.StdOut.Contains("An unexpected error occurred while executing the command"));
}
Expand All @@ -50,16 +50,16 @@ public void SourceListWithNoArgs()
// List with no args should list all available sources
var result = TestCommon.RunAICLICommand("source list", "");
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
Assert.True(result.StdOut.Contains("https://localhost:5001/TestKit"));
Assert.True(result.StdOut.Contains(Constants.TestSourceUrl));
}

[Test]
public void SourceListWithName()
{
var result = TestCommon.RunAICLICommand("source list", $"-n {Constants.TestSourceName}");
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
Assert.True(result.StdOut.Contains("TestSource"));
Assert.True(result.StdOut.Contains("https://localhost:5001/TestKit"));
Assert.True(result.StdOut.Contains(Constants.TestSourceName));
Assert.True(result.StdOut.Contains(Constants.TestSourceUrl));
Assert.True(result.StdOut.Contains("Microsoft.PreIndexed.Package"));
Assert.True(result.StdOut.Contains("Updated"));
}
Expand Down Expand Up @@ -110,8 +110,8 @@ public void SourceReset()
{
var result = TestCommon.RunAICLICommand("source reset", "");
Assert.True(result.StdOut.Contains("The following sources will be reset if the --force option is given:"));
Assert.True(result.StdOut.Contains("TestSource"));
Assert.True(result.StdOut.Contains("https://localhost:5001/TestKit"));
Assert.True(result.StdOut.Contains(Constants.TestSourceName));
Assert.True(result.StdOut.Contains(Constants.TestSourceUrl));
}

[Test]
Expand All @@ -126,8 +126,8 @@ public void SourceForceReset()
result = TestCommon.RunAICLICommand("source list", "");
Assert.True(result.StdOut.Contains("winget"));
Assert.True(result.StdOut.Contains("https://winget.azureedge.net/cache"));
Assert.False(result.StdOut.Contains($"{Constants.TestSourceName}"));
Assert.False(result.StdOut.Contains($"{Constants.TestSourceUrl}"));
Assert.False(result.StdOut.Contains(Constants.TestSourceName));
Assert.False(result.StdOut.Contains(Constants.TestSourceUrl));
ResetTestSource();
}
}
Expand Down
17 changes: 17 additions & 0 deletions src/AppInstallerCLITests/Strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "pch.h"
#include "TestCommon.h"
#include <AppInstallerStrings.h>
#include <ExecutionReporter.h>

using namespace std::string_view_literals;
using namespace AppInstaller::Utility;
Expand Down Expand Up @@ -147,3 +148,19 @@ TEST_CASE("ExpandEnvironmentVariables", "[strings]")

REQUIRE(ExpandEnvironmentVariables(L"%TEMP%") == tempPath);
}

TEST_CASE("PathOutput", "[strings]")
{
std::string original = "\xe6\xb5\x8b\xe8\xaf\x95";
std::filesystem::path path = ConvertToUTF16(original);
AICLI_LOG(Test, Info, << path);

std::istringstream in;
std::ostringstream out;
AppInstaller::CLI::Execution::Reporter reporter{ out, in };

reporter.Info() << path;

std::string output = out.str();
REQUIRE(output.substr(output.size() - original.size()) == original);
}
33 changes: 32 additions & 1 deletion src/AppInstallerCommonCore/Public/AppInstallerLogging.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
auto& _aicli_log_log = AppInstaller::Logging::Log(); \
if (_aicli_log_log.IsEnabled(_aicli_log_channel, _aicli_log_level)) \
{ \
std::stringstream _aicli_log_strstr; \
AppInstaller::Logging::LoggingStream _aicli_log_strstr; \
_aicli_log_strstr _outstream_; \
_aicli_log_log.Write(_aicli_log_channel, _aicli_log_level, _aicli_log_strstr.str()); \
} \
Expand Down Expand Up @@ -140,6 +140,37 @@ namespace AppInstaller::Logging

// Calls the various stream format functions to produce an 8 character hexadecimal output.
std::ostream& SetHRFormat(std::ostream& out);

// This type allows us to override the default behavior of output operators for logging.
struct LoggingStream
{
// Force use of the UTF-8 string from a file path.
// This should not be necessary when we move to C++20 and convert to using u8string.
friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, std::filesystem::path& path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::filesystem::path& path [](start = 107, length = 27)

Is this override with non const variant used?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes; when doing overload resolution, only the exact match will be considered a better candidate than the template. So a non-const local will not match the const& function. Unfortunately I'm not aware of a better way to force the compiler to "do everything as you would have except in this one case". I suppose it might be possible to add some dummy parameters to the template to make it a worse candidate function.

{
out.m_out << path.u8string();
return out;
}

friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, const std::filesystem::path& path)
{
out.m_out << path.u8string();
return out;
}

// Everything else.
template <typename T>
friend AppInstaller::Logging::LoggingStream& operator<<(AppInstaller::Logging::LoggingStream& out, T&& t)
{
out.m_out << std::forward<T>(t);
return out;
}

std::string str() const { return m_out.str(); }

private:
std::stringstream m_out;
};
}

// Enable output of system_clock time_points.
Expand Down
10 changes: 5 additions & 5 deletions src/AppInstallerCommonCore/Public/winget/LocIndependent.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ namespace AppInstaller::Utility

bool operator<(const LocIndString& other) const { return m_value < other.m_value; }

friend std::ostream& operator<<(std::ostream& out, const AppInstaller::Utility::LocIndString& lis)
{
return (out << lis.get());
}

private:
std::string m_value;
};
}

inline std::ostream& operator<<(std::ostream& out, const AppInstaller::Utility::LocIndString& lis)
{
return (out << lis.get());
}