Files
carbon-lang/common/string_helpers_test.cpp
T
Chandler CarruthandGeoff Romer ff8ce31e1b Factor out C-string argv building and simplify vlogs (#6478)
This extracts the C-string `argv`-like building routine to a more
broadly reusable location. It also sinks the verbose logging logic out
of it and into the relevant runners. In turn, it simplifies the verbose
logging logic significantly.

The biggest functional change is removing the implicit synthesis of a
tool's `-v` verbose flag from the presence of a `vlog` stream. I thought
this would be helpful, but in practice of debugging these layers it has
been more of a hindrance than a help -- I pretty often only want verbose
logging on one side or the other, and we have ways of explicitly passing
a `-v` flag to the underlying tools already. I think my instinct to do
this was just wrong, so rip it out and simplify.

This does add an unused feature -- prepending a prefix of arguments
while building the C-string variant. This isn't used in this PR but will
be used in subsequent PRs and it seemed more disruptive to undo that
logic and then re-do it in a later PR. Let me know if it's too confusing
here.

Assisted-by: Gemini Code Assist

---------

Co-authored-by: Geoff Romer <gromer@google.com>
2025-12-10 22:07:01 +00:00

299 lines
9.5 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "common/string_helpers.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <optional>
#include <string>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
using ::testing::Eq;
using ::testing::Optional;
using ::testing::StrEq;
namespace Carbon {
namespace {
TEST(UnescapeStringLiteral, Valid) {
EXPECT_THAT(UnescapeStringLiteral("test"), Optional(Eq("test")));
EXPECT_THAT(UnescapeStringLiteral("okay whitespace"),
Optional(Eq("okay whitespace")));
EXPECT_THAT(UnescapeStringLiteral("test\n"), Optional(Eq("test\n")));
EXPECT_THAT(UnescapeStringLiteral("test\\n"), Optional(Eq("test\n")));
EXPECT_THAT(UnescapeStringLiteral("abc\\ndef"), Optional(Eq("abc\ndef")));
EXPECT_THAT(UnescapeStringLiteral("test\\\\n"), Optional(Eq("test\\n")));
EXPECT_THAT(UnescapeStringLiteral("\\xAA"), Optional(Eq("\xAA")));
EXPECT_THAT(UnescapeStringLiteral("\\x12"), Optional(Eq("\x12")));
EXPECT_THAT(UnescapeStringLiteral("test", 1), Optional(Eq("test")));
EXPECT_THAT(UnescapeStringLiteral("test\\#n", 1), Optional(Eq("test\n")));
EXPECT_THAT(UnescapeStringLiteral(
"r\\u{000000E9}al \\u{2764}\\u{FE0F}\\u{1F50A}!\\u{10FFFF}"),
Optional(Eq("réal ❤️🔊!􏿿")));
}
TEST(UnescapeStringLiteral, Invalid) {
// Missing char after `\`.
EXPECT_THAT(UnescapeStringLiteral("a\\"), Eq(std::nullopt));
// Not a supported escape.
EXPECT_THAT(UnescapeStringLiteral("\\e"), Eq(std::nullopt));
// Needs 2 hex chars.
EXPECT_THAT(UnescapeStringLiteral("\\x"), Eq(std::nullopt));
// Needs 2 hex chars.
EXPECT_THAT(UnescapeStringLiteral("\\xA"), Eq(std::nullopt));
// Needs uppercase hex.
EXPECT_THAT(UnescapeStringLiteral("\\xaa"), Eq(std::nullopt));
// Reserved.
EXPECT_THAT(UnescapeStringLiteral("\\00"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\#00", 1), Eq(std::nullopt));
}
TEST(UnescapeStringLiteral, InvalidUnicodes) {
// Various incomplete Unicode specifiers
EXPECT_THAT(UnescapeStringLiteral("\\u"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\u1"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\uz"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\u{"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\u{z"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\u{E9"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\u{E9z"), Eq(std::nullopt));
EXPECT_THAT(UnescapeStringLiteral("\\u{}"), Eq(std::nullopt));
// invalid characters in unicode
EXPECT_THAT(UnescapeStringLiteral("\\u{z}"), Eq(std::nullopt));
// lowercase hexadecimal
EXPECT_THAT(UnescapeStringLiteral("\\u{e9}"), Eq(std::nullopt));
// Codepoint number too high
EXPECT_THAT(UnescapeStringLiteral("\\u{110000}"), Eq(std::nullopt));
// codepoint more than 8 hex digits
EXPECT_THAT(UnescapeStringLiteral("\\u{FF000000E9}"), Eq(std::nullopt));
}
TEST(UnescapeStringLiteral, Nul) {
std::optional<std::string> str = UnescapeStringLiteral("a\\0b");
ASSERT_NE(str, std::nullopt);
EXPECT_THAT(str->size(), Eq(3));
EXPECT_THAT(strlen(str->c_str()), Eq(1));
EXPECT_THAT((*str)[0], Eq('a'));
EXPECT_THAT((*str)[1], Eq('\0'));
EXPECT_THAT((*str)[2], Eq('b'));
}
TEST(ParseBlockStringLiteral, FailTooFewLines) {
EXPECT_THAT(ParseBlockStringLiteral("").error().message(),
Eq("Too few lines"));
}
TEST(ParseBlockStringLiteral, FailNoLeadingTripleQuotes) {
EXPECT_THAT(ParseBlockStringLiteral("'a'\n").error().message(),
Eq("Should start with triple quotes: 'a'"));
}
TEST(ParseBlockStringLiteral, FailInvalideFiletypeIndicator) {
EXPECT_THAT(ParseBlockStringLiteral("'''carbon file\n").error().message(),
Eq("Invalid characters in file type indicator: carbon file"));
}
TEST(ParseBlockStringLiteral, FailEndingTripleQuotes) {
EXPECT_THAT(ParseBlockStringLiteral("'''\n").error().message(),
Eq("Should end with triple quotes: "));
}
TEST(ParseBlockStringLiteral, FailWrongIndent) {
constexpr char Input[] = R"('''
A block string literal
with wrong indent
''')";
EXPECT_THAT(ParseBlockStringLiteral(Input).error().message(),
Eq("Wrong indent for line: with wrong indent, expected 5"));
}
TEST(ParseBlockStringLiteral, FailInvalidEscaping) {
constexpr char Input[] = R"('''
\q
''')";
EXPECT_THAT(ParseBlockStringLiteral(Input).error().message(),
Eq("Invalid escaping in \\q"));
constexpr char InputRaw[] = R"('''
\#q
''')";
EXPECT_THAT(ParseBlockStringLiteral(InputRaw, 1).error().message(),
Eq("Invalid escaping in \\#q"));
}
TEST(ParseBlockStringLiteral, OkEmptyString) {
constexpr char Input[] = R"('''
''')";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(""));
}
TEST(ParseBlockStringLiteral, OkOneLineString) {
constexpr char Input[] = R"('''
A block string literal
''')";
constexpr char Expected[] = R"(A block string literal
)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkTwoLineString) {
constexpr char Input[] = R"('''
A block string literal
with indent.
''')";
constexpr char Expected[] = R"(A block string literal
with indent.
)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkWithFileTypeIndicator) {
constexpr char Input[] = R"('''carbon
A block string literal
with file type indicator.
''')";
constexpr char Expected[] = R"(A block string literal
with file type indicator.
)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkWhitespaceAfterOpeningQuotes) {
constexpr char Input[] = R"('''
A block string literal
''')";
constexpr char Expected[] = R"(A block string literal
)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkWithEmptyLines) {
constexpr char Input[] = R"('''
A block string literal
with
empty
lines.
''')";
constexpr char Expected[] = R"(A block string literal
with
empty
lines.
)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkWithSlashNewlineEscape) {
constexpr char Input[] = R"('''
A block string literal\
''')";
constexpr char Expected[] = "A block string literal";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkWithDoubleSlashNewline) {
constexpr char Input[] = R"('''
A block string literal\\
''')";
constexpr char Expected[] = R"(A block string literal\
)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkWithTripleSlashNewline) {
constexpr char Input[] = R"('''
A block string literal\\\
''')";
constexpr char Expected[] = R"(A block string literal\)";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(ParseBlockStringLiteral, OkMultipleSlashes) {
constexpr char Input[] = R"('''
A block string literal\
\
\
\
''')";
constexpr char Expected[] = "A block string literal";
EXPECT_THAT(*ParseBlockStringLiteral(Input), Eq(Expected));
}
TEST(BuildCStrArgs, NoArgs) {
llvm::OwningArrayRef<char> storage;
auto result = BuildCStrArgs("tool", {}, storage);
ASSERT_THAT(result.size(), Eq(1));
EXPECT_THAT(result[0], StrEq("tool"));
}
TEST(BuildCStrArgs, OneArg) {
llvm::OwningArrayRef<char> storage;
auto result = BuildCStrArgs("tool", {"arg1"}, storage);
ASSERT_THAT(result.size(), Eq(2));
EXPECT_THAT(result[0], StrEq("tool"));
EXPECT_THAT(result[1], StrEq("arg1"));
}
TEST(BuildCStrArgs, MultipleArgs) {
llvm::OwningArrayRef<char> storage;
auto result = BuildCStrArgs("tool", {"arg1", "arg2"}, storage);
ASSERT_THAT(result.size(), Eq(3));
EXPECT_THAT(result[0], StrEq("tool"));
EXPECT_THAT(result[1], StrEq("arg1"));
EXPECT_THAT(result[2], StrEq("arg2"));
}
TEST(BuildCStrArgsWithPrefix, NoArgs) {
llvm::OwningArrayRef<char> storage;
auto result = BuildCStrArgs("tool", {}, {}, storage);
ASSERT_THAT(result.size(), Eq(1));
EXPECT_THAT(result[0], StrEq("tool"));
}
TEST(BuildCStrArgsWithPrefix, PrefixOnly) {
llvm::OwningArrayRef<char> storage;
std::string prefix_args[] = {"p_arg1", "p_arg2"};
auto result = BuildCStrArgs("tool", prefix_args, {}, storage);
ASSERT_THAT(result.size(), Eq(3));
EXPECT_THAT(result[0], StrEq("tool"));
EXPECT_THAT(result[1], Eq(prefix_args[0].c_str()));
EXPECT_THAT(result[2], Eq(prefix_args[1].c_str()));
}
TEST(BuildCStrArgsWithPrefix, ArgsOnly) {
llvm::OwningArrayRef<char> storage;
auto result = BuildCStrArgs("tool", {}, {"arg1", "arg2"}, storage);
ASSERT_THAT(result.size(), Eq(3));
EXPECT_THAT(result[0], StrEq("tool"));
EXPECT_THAT(result[1], StrEq("arg1"));
EXPECT_THAT(result[2], StrEq("arg2"));
}
TEST(BuildCStrArgsWithPrefix, BothPrefixAndArgs) {
llvm::OwningArrayRef<char> storage;
std::string prefix_args[] = {"p_arg1", "p_arg2"};
auto result = BuildCStrArgs("tool", prefix_args, {"arg1", "arg2"}, storage);
ASSERT_THAT(result.size(), Eq(5));
EXPECT_THAT(result[0], StrEq("tool"));
EXPECT_THAT(result[1], Eq(prefix_args[0].c_str()));
EXPECT_THAT(result[2], Eq(prefix_args[1].c_str()));
EXPECT_THAT(result[3], StrEq("arg1"));
EXPECT_THAT(result[4], StrEq("arg2"));
}
} // namespace
} // namespace Carbon