blob: d281a7ea1018dc754ebb27eefbe75150d4aeb201 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/expr/pretty_std_string.h"
#include <gtest/gtest.h>
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/zxdb/common/test_with_loop.h"
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/developer/debug/zxdb/expr/format_node.h"
#include "src/developer/debug/zxdb/expr/format_options.h"
#include "src/developer/debug/zxdb/expr/mock_eval_context.h"
#include "src/developer/debug/zxdb/symbols/type_test_support.h"
namespace zxdb {
namespace {
// Memory address/length of the default string. This is relatively long so it's guaranteed to
// exceed the C++ short string optimization length.
constexpr uint64_t kStringAddress = 0x99887766;
constexpr uint64_t kStringLen = 69; // Not including null.
class PrettyStringTest : public TestWithLoop {
public:
PrettyStringTest() {
context_ = fxl::MakeRefCounted<MockEvalContext>();
const char kStringData[] =
"Now is the time for all good men to come to the aid of their country.";
context_->data_provider()->AddMemory(
kStringAddress, std::vector<uint8_t>(std::begin(kStringData), std::end(kStringData)));
}
// Calls the given getter, promotes the result to 64-bit and expects that it's equal to the given
// value.
void ExpectGetterReturns(const ExprValue& input, const std::string& getter_name,
uint64_t expected) {
PrettyStdString pretty;
auto getter = pretty.GetGetter(getter_name);
ASSERT_TRUE(getter);
bool should_quit = false;
bool called = false;
getter(context(), input, [&called, &should_quit, expected](ErrOrValue v) {
called = true;
ASSERT_TRUE(v.ok()) << v.err().msg();
uint64_t actual = 0;
Err err = v.value().PromoteTo64(&actual);
ASSERT_TRUE(err.ok());
EXPECT_EQ(expected, actual);
if (should_quit)
debug_ipc::MessageLoop::Current()->QuitNow();
});
if (!called) {
should_quit = true;
debug_ipc::MessageLoop::Current()->Run();
}
EXPECT_TRUE(called);
}
fxl::RefPtr<MockEvalContext> context() { return context_; }
private:
fxl::RefPtr<MockEvalContext> context_;
};
} // namespace
TEST_F(PrettyStringTest, StdStringShort) {
// Encodes 'a'-'m' in the "short" form of a std::string.
uint8_t kShortMem[24] = {
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, // Inline bytes...
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x00, 0x00, 0x00, // ...
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, // ..., last byte is size.
};
ExprValue short_value(fxl::RefPtr<Type>(),
std::vector<uint8_t>(std::begin(kShortMem), std::end(kShortMem)));
FormatNode node("value", short_value);
PrettyStdString pretty;
// Expect synchronous completion for the inline value case.
bool completed = false;
pretty.Format(&node, FormatOptions(), context(),
fit::defer_callback([&completed]() { completed = true; }));
EXPECT_TRUE(completed);
EXPECT_EQ("\"abcdefghijklm\"", node.description());
ExpectGetterReturns(short_value, "size", 13);
ExpectGetterReturns(short_value, "length", 13);
ExpectGetterReturns(short_value, "capacity", 22); // 24 bytes - size - null.
ExpectGetterReturns(short_value, "empty", 0);
}
// Tests a string with no bytes but a source location. This string data encodes the "long" format
// which is in turn another pointer.
TEST_F(PrettyStringTest, StdStringLong) {
// The std::string object representation.
constexpr uint64_t kObjectAddress = 0x12345678;
uint8_t kLongMem[24] = {
// clang-format off
0x66, 0x77, 0x88, 0x99, 0x00, 0x00, 0x00, 0x00, // Address = kStringAddress.
kStringLen, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Length = kStringLen
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // Capacity (last bit is "long" flag).
// clang-format on
};
context()->data_provider()->AddMemory(
kObjectAddress, std::vector<uint8_t>(std::begin(kLongMem), std::end(kLongMem)));
// The std::string object. This has no data in it because the real type isn't known, but it does
// have a valid source address.
ExprValue value(fxl::RefPtr<Type>(), {}, ExprValueSource(kObjectAddress));
FormatNode node("value", value);
PrettyStdString pretty;
bool completed = false;
pretty.Format(&node, FormatOptions(), context(),
fit::defer_callback([&completed, loop = &loop()]() {
completed = true;
loop->QuitNow();
}));
EXPECT_FALSE(completed); // Should be async.
loop().Run();
EXPECT_TRUE(completed);
EXPECT_EQ("\"Now is the time for all good men to come to the aid of their country.\"",
node.description());
ExpectGetterReturns(value, "size", kStringLen);
ExpectGetterReturns(value, "length", kStringLen);
ExpectGetterReturns(value, "capacity", 0x50);
ExpectGetterReturns(value, "empty", 0);
}
} // namespace zxdb