blob: 8e161eb78f50ad5fd5a3791fa7f8965d7e7eb7ec [file] [edit]
//===- TestStringRef.cpp - Tests for kmp_str_ref class -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "kmp_adt.h"
#include "kmp.h"
#include "gtest/gtest.h"
#include <cstring>
namespace {
// Helper to compare kmp_str_ref content with a C string
static bool equals(const kmp_str_ref &s, const char *expected) {
size_t expected_len = strlen(expected);
if (s.length() != expected_len)
return false;
return memcmp(s.begin(), expected, expected_len) == 0;
}
//===----------------------------------------------------------------------===//
// Construction and Basic Properties
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, ConstructFromCString) {
kmp_str_ref s("Hello");
EXPECT_EQ(s.length(), 5u);
EXPECT_TRUE(equals(s, "Hello"));
}
TEST(kmp_str_ref_test, ConstructFromCStringWithLength) {
kmp_str_ref s("Hello World", 5);
EXPECT_EQ(s.length(), 5u);
EXPECT_TRUE(equals(s, "Hello"));
}
TEST(kmp_str_ref_test, ConstructEmpty) {
kmp_str_ref s("");
EXPECT_EQ(s.length(), 0u);
EXPECT_TRUE(s.empty());
}
TEST(kmp_str_ref_test, ConstructFromNullptr) {
const char *null_str = nullptr;
kmp_str_ref s(null_str);
EXPECT_EQ(s.length(), 0u);
EXPECT_TRUE(s.empty());
}
TEST(kmp_str_ref_test, ConstructFromNullptrWithLength) {
kmp_str_ref s(nullptr, 0);
EXPECT_EQ(s.length(), 0u);
EXPECT_TRUE(s.empty());
}
TEST(kmp_str_ref_test, NullptrOperations) {
const char *null_str = nullptr;
kmp_str_ref s(null_str);
// Iterators on a null-backed string must be consistent and yield no
// elements.
EXPECT_EQ(s.begin(), s.end());
EXPECT_EQ(s.end() - s.begin(), 0);
std::string result;
for (char c : s)
result += c;
EXPECT_TRUE(result.empty());
// Consuming the empty prefix must succeed and leave the string empty.
EXPECT_TRUE(s.consume_front(""));
EXPECT_TRUE(s.empty());
// Consuming a non-empty prefix must fail without dereferencing data.
EXPECT_FALSE(s.consume_front("x"));
// count_while must not dereference data when the string is empty.
EXPECT_EQ(s.count_while([](char) { return true; }), 0u);
}
TEST(kmp_str_ref_test, Length) {
EXPECT_EQ(kmp_str_ref("").length(), 0u);
EXPECT_EQ(kmp_str_ref("a").length(), 1u);
EXPECT_EQ(kmp_str_ref("hello").length(), 5u);
EXPECT_EQ(kmp_str_ref("hello world").length(), 11u);
}
TEST(kmp_str_ref_test, Size) {
EXPECT_EQ(kmp_str_ref("").size(), 0u);
EXPECT_EQ(kmp_str_ref("a").size(), 1u);
EXPECT_EQ(kmp_str_ref("hello").size(), 5u);
EXPECT_EQ(kmp_str_ref("hello world").size(), 11u);
}
//===----------------------------------------------------------------------===//
// empty
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, EmptyString) {
kmp_str_ref s("");
EXPECT_TRUE(s.empty());
}
TEST(kmp_str_ref_test, NonEmptyString) {
kmp_str_ref s("hello");
EXPECT_FALSE(s.empty());
}
TEST(kmp_str_ref_test, EmptyAfterConsumeFront) {
kmp_str_ref s("hello");
EXPECT_FALSE(s.empty());
s.consume_front("hello");
EXPECT_TRUE(s.empty());
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, EmptyAfterDropFront) {
kmp_str_ref s("abc");
EXPECT_FALSE(s.empty());
s.drop_front(3);
EXPECT_TRUE(s.empty());
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, EmptyAfterDropWhile) {
kmp_str_ref s("12345");
EXPECT_FALSE(s.empty());
s.drop_while([](char c) {
return static_cast<bool>(isdigit(static_cast<unsigned char>(c)));
});
EXPECT_TRUE(s.empty());
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, EmptyAfterConsumeInteger) {
kmp_str_ref s("42");
int value = 0;
EXPECT_FALSE(s.empty());
s.consume_integer(value);
EXPECT_TRUE(s.empty());
EXPECT_EQ(s.length(), 0u);
EXPECT_EQ(value, 42);
}
TEST(kmp_str_ref_test, NotEmptyAfterPartialConsume) {
kmp_str_ref s("123abc");
int value = 0;
s.consume_integer(value);
EXPECT_FALSE(s.empty());
EXPECT_EQ(s.length(), 3u);
EXPECT_TRUE(equals(s, "abc"));
}
//===----------------------------------------------------------------------===//
// Iterators
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, BeginEnd) {
kmp_str_ref s("Hello");
EXPECT_EQ(s.end() - s.begin(), 5);
EXPECT_EQ(*s.begin(), 'H');
}
TEST(kmp_str_ref_test, RangeBasedFor) {
kmp_str_ref s("abc");
std::string result;
for (char c : s) {
result += c;
}
EXPECT_EQ(result, "abc");
}
//===----------------------------------------------------------------------===//
// Assignment
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, Assignment) {
kmp_str_ref s1("First");
kmp_str_ref s2("Second");
s1 = s2;
EXPECT_TRUE(equals(s1, "Second"));
EXPECT_EQ(s1.length(), 6u);
}
TEST(kmp_str_ref_test, SelfAssignment) {
kmp_str_ref s("Test");
kmp_str_ref &s_ref = s;
s = s_ref; // Avoid self-assignment warning
EXPECT_TRUE(equals(s, "Test"));
EXPECT_EQ(s.length(), 4u);
}
//===----------------------------------------------------------------------===//
// consume_front
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, ConsumeFrontSuccess) {
kmp_str_ref s("Hello World");
EXPECT_TRUE(s.consume_front("Hello"));
EXPECT_EQ(s.length(), 6u);
EXPECT_TRUE(equals(s, " World"));
}
TEST(kmp_str_ref_test, ConsumeFrontFailure) {
kmp_str_ref s("Hello World");
EXPECT_FALSE(s.consume_front("World"));
EXPECT_EQ(s.length(), 11u);
EXPECT_TRUE(equals(s, "Hello World"));
}
TEST(kmp_str_ref_test, ConsumeFrontEmpty) {
kmp_str_ref s("Hello");
EXPECT_TRUE(s.consume_front(""));
EXPECT_EQ(s.length(), 5u);
}
TEST(kmp_str_ref_test, ConsumeFrontTooLong) {
kmp_str_ref s("Hi");
EXPECT_FALSE(s.consume_front("Hello"));
EXPECT_EQ(s.length(), 2u);
}
TEST(kmp_str_ref_test, ConsumeFrontExact) {
kmp_str_ref s("Hello");
EXPECT_TRUE(s.consume_front("Hello"));
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, ConsumeFrontMultiple) {
kmp_str_ref s("prefix:middle:suffix");
EXPECT_TRUE(s.consume_front("prefix"));
EXPECT_TRUE(s.consume_front(":"));
EXPECT_TRUE(s.consume_front("middle"));
EXPECT_TRUE(s.consume_front(":"));
EXPECT_TRUE(equals(s, "suffix"));
}
//===----------------------------------------------------------------------===//
// consume_integer
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, ConsumeIntegerSimple) {
kmp_str_ref s("42");
int value = 0;
EXPECT_TRUE(s.consume_integer(value));
EXPECT_EQ(value, 42);
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, ConsumeIntegerWithTrailing) {
kmp_str_ref s("123abc");
int value = 0;
EXPECT_TRUE(s.consume_integer(value));
EXPECT_EQ(value, 123);
EXPECT_TRUE(equals(s, "abc"));
}
TEST(kmp_str_ref_test, ConsumeIntegerZero) {
kmp_str_ref s("0");
int value = -1;
// allow_zero = true by default
EXPECT_TRUE(s.consume_integer(value));
EXPECT_EQ(value, 0);
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, ConsumeIntegerZeroNotAllowed) {
kmp_str_ref s("0rest");
int value = -1;
EXPECT_FALSE(s.consume_integer(value, /*allow_zero=*/false));
// State should be restored on failure
EXPECT_TRUE(equals(s, "0rest"));
}
TEST(kmp_str_ref_test, ConsumeIntegerNoDigits) {
kmp_str_ref s("abc");
int value = -1;
// No digits to consume, should fail
EXPECT_FALSE(s.consume_integer(value));
// String should be unchanged
EXPECT_TRUE(equals(s, "abc"));
}
TEST(kmp_str_ref_test, ConsumeIntegerEmpty) {
kmp_str_ref s("");
int value = -1;
// Empty string has no digits, should fail
EXPECT_FALSE(s.consume_integer(value));
}
TEST(kmp_str_ref_test, ConsumeIntegerLeadingZero) {
kmp_str_ref s("007");
int value = -1;
EXPECT_TRUE(s.consume_integer(value));
EXPECT_EQ(value, 7);
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, ConsumeIntegerNegativeAllowed) {
kmp_str_ref s("-42rest");
int value = 0;
EXPECT_TRUE(s.consume_integer(value, true, true));
EXPECT_EQ(value, -42);
EXPECT_TRUE(equals(s, "rest"));
}
TEST(kmp_str_ref_test, ConsumeIntegerNegativeNotAllowed) {
kmp_str_ref s("-42");
int value = 0;
EXPECT_FALSE(s.consume_integer(value, true, false));
// State should be restored on failure
EXPECT_TRUE(equals(s, "-42"));
}
TEST(kmp_str_ref_test, ConsumeIntegerMultipleDigits) {
kmp_str_ref s("1234567890");
int value = 0;
EXPECT_TRUE(s.consume_integer(value));
EXPECT_EQ(value, 1234567890);
}
//===----------------------------------------------------------------------===//
// copy
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, Copy) {
kmp_str_ref s("Hello");
char *copied = s.copy();
EXPECT_NE(copied, nullptr);
EXPECT_STREQ(copied, "Hello");
EXPECT_NE(copied, s.begin()); // Different pointer
KMP_INTERNAL_FREE(copied);
}
TEST(kmp_str_ref_test, CopyEmpty) {
kmp_str_ref s("");
char *copied = s.copy();
EXPECT_NE(copied, nullptr);
EXPECT_STREQ(copied, "");
KMP_INTERNAL_FREE(copied);
}
TEST(kmp_str_ref_test, CopySubstring) {
// Test copying a substring that doesn't have a null terminator at len
kmp_str_ref full("device-0)rest");
kmp_str_ref sub = full.take_while([](char c) { return c != ')'; });
EXPECT_EQ(sub.length(), 8u); // "device-0"
char *copied = sub.copy();
EXPECT_NE(copied, nullptr);
EXPECT_STREQ(copied, "device-0"); // Should NOT include ")"
EXPECT_EQ(strlen(copied), 8u);
KMP_INTERNAL_FREE(copied);
}
//===----------------------------------------------------------------------===//
// drop_front
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, DropFront) {
kmp_str_ref s("Hello World");
s.drop_front(6);
EXPECT_EQ(s.length(), 5u);
EXPECT_TRUE(equals(s, "World"));
}
TEST(kmp_str_ref_test, DropFrontZero) {
kmp_str_ref s("Hello");
s.drop_front(0);
EXPECT_EQ(s.length(), 5u);
EXPECT_TRUE(equals(s, "Hello"));
}
TEST(kmp_str_ref_test, DropFrontAll) {
kmp_str_ref s("Hello");
s.drop_front(5);
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, DropFrontMoreThanLength) {
kmp_str_ref s("Hi");
s.drop_front(100);
EXPECT_EQ(s.length(), 0u);
}
//===----------------------------------------------------------------------===//
// drop_while
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, DropWhileDigits) {
kmp_str_ref s("123abc");
s.drop_while([](char c) {
return static_cast<bool>(isdigit(static_cast<unsigned char>(c)));
});
EXPECT_TRUE(equals(s, "abc"));
}
TEST(kmp_str_ref_test, DropWhileSpaces) {
kmp_str_ref s(" hello");
s.drop_while([](char c) { return c == ' '; });
EXPECT_TRUE(equals(s, "hello"));
}
TEST(kmp_str_ref_test, DropWhileNone) {
kmp_str_ref s("hello");
s.drop_while([](char c) { return c == ' '; });
EXPECT_TRUE(equals(s, "hello"));
}
TEST(kmp_str_ref_test, DropWhileAll) {
kmp_str_ref s("12345");
s.drop_while([](char c) {
return static_cast<bool>(isdigit(static_cast<unsigned char>(c)));
});
EXPECT_EQ(s.length(), 0u);
}
//===----------------------------------------------------------------------===//
// count_while
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, CountWhileDigits) {
kmp_str_ref s("123abc");
size_t n = s.count_while(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(n, 3u);
}
TEST(kmp_str_ref_test, CountWhileAll) {
kmp_str_ref s("12345");
size_t n = s.count_while(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(n, s.length());
}
TEST(kmp_str_ref_test, CountWhileNone) {
kmp_str_ref s("abc");
size_t n = s.count_while(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(n, 0u);
}
TEST(kmp_str_ref_test, CountWhileEmpty) {
kmp_str_ref s("");
size_t n = s.count_while([](char c) { return c == 'a'; });
EXPECT_EQ(n, 0u);
}
//===----------------------------------------------------------------------===//
// find_if
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, FindIfDigit) {
kmp_str_ref s("abc123");
size_t i = s.find_if(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(i, 3u);
}
TEST(kmp_str_ref_test, FindIfFirstChar) {
kmp_str_ref s("hello");
size_t i = s.find_if([](char c) { return c == 'h'; });
EXPECT_EQ(i, 0u);
}
TEST(kmp_str_ref_test, FindIfLastChar) {
kmp_str_ref s("hello");
size_t i = s.find_if([](char c) { return c == 'o'; });
EXPECT_EQ(i, 4u);
}
TEST(kmp_str_ref_test, FindIfNoMatch) {
kmp_str_ref s("hello");
size_t i = s.find_if(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(i, kmp_str_ref::npos);
}
TEST(kmp_str_ref_test, FindIfEmpty) {
kmp_str_ref s("");
size_t i = s.find_if([](char c) { return c == 'a'; });
EXPECT_EQ(i, kmp_str_ref::npos);
}
//===----------------------------------------------------------------------===//
// find_if_not
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, FindIfNotDigit) {
kmp_str_ref s("123abc");
size_t i = s.find_if_not(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(i, 3u);
}
TEST(kmp_str_ref_test, FindIfNotAllMatch) {
kmp_str_ref s("12345");
size_t i = s.find_if_not(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(i, kmp_str_ref::npos);
}
TEST(kmp_str_ref_test, FindIfNotNoneMatch) {
kmp_str_ref s("abc");
size_t i = s.find_if_not(
[](char c) { return isdigit(static_cast<unsigned char>(c)) != 0; });
EXPECT_EQ(i, 0u);
}
TEST(kmp_str_ref_test, FindIfNotEmpty) {
kmp_str_ref s("");
size_t i = s.find_if_not([](char c) { return c == 'a'; });
EXPECT_EQ(i, kmp_str_ref::npos);
}
//===----------------------------------------------------------------------===//
// skip_space
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, SkipSpace) {
kmp_str_ref s(" hello");
s.skip_space();
EXPECT_TRUE(equals(s, "hello"));
}
TEST(kmp_str_ref_test, SkipSpaceNoSpaces) {
kmp_str_ref s("hello");
s.skip_space();
EXPECT_TRUE(equals(s, "hello"));
}
TEST(kmp_str_ref_test, SkipSpaceAllSpaces) {
kmp_str_ref s(" ");
s.skip_space();
EXPECT_EQ(s.length(), 0u);
}
TEST(kmp_str_ref_test, SkipSpaceOnlyLeading) {
kmp_str_ref s(" hello world ");
s.skip_space();
EXPECT_TRUE(equals(s, "hello world "));
}
TEST(kmp_str_ref_test, SkipSpaceWithTabs) {
kmp_str_ref s("\t\n hello");
s.skip_space();
EXPECT_TRUE(equals(s, "hello"));
}
//===----------------------------------------------------------------------===//
// take_while
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, TakeWhileDigits) {
kmp_str_ref s("123abc");
kmp_str_ref digits = s.take_while([](char c) {
return static_cast<bool>(isdigit(static_cast<unsigned char>(c)));
});
EXPECT_EQ(digits.length(), 3u);
EXPECT_TRUE(equals(digits, "123"));
// Original unchanged
EXPECT_EQ(s.length(), 6u);
}
TEST(kmp_str_ref_test, TakeWhileAlpha) {
kmp_str_ref s("hello123");
kmp_str_ref alpha = s.take_while([](char c) {
return static_cast<bool>(isalpha(static_cast<unsigned char>(c)));
});
EXPECT_EQ(alpha.length(), 5u);
EXPECT_TRUE(equals(alpha, "hello"));
}
TEST(kmp_str_ref_test, TakeWhileNone) {
kmp_str_ref s("123abc");
kmp_str_ref result = s.take_while([](char c) {
return static_cast<bool>(isalpha(static_cast<unsigned char>(c)));
});
EXPECT_EQ(result.length(), 0u);
}
TEST(kmp_str_ref_test, TakeWhileAll) {
kmp_str_ref s("hello");
kmp_str_ref result = s.take_while([](char c) {
return static_cast<bool>(isalpha(static_cast<unsigned char>(c)));
});
EXPECT_EQ(result.length(), 5u);
EXPECT_TRUE(equals(result, "hello"));
}
//===----------------------------------------------------------------------===//
// Integration / Complex Scenarios
//===----------------------------------------------------------------------===//
TEST(kmp_str_ref_test, ParseKeyValuePair) {
kmp_str_ref s("key=value");
kmp_str_ref key = s.take_while([](char c) { return c != '='; });
s.drop_front(key.length());
s.consume_front("=");
EXPECT_EQ(key.length(), 3u);
EXPECT_TRUE(equals(key, "key"));
EXPECT_TRUE(equals(s, "value"));
}
TEST(kmp_str_ref_test, ParseCommaSeparated) {
kmp_str_ref s("1,2,3");
int values[3] = {0, 0, 0};
int count = 0;
while (s.length() > 0 && count < 3) {
s.consume_integer(values[count++]);
s.consume_front(",");
}
EXPECT_EQ(count, 3);
EXPECT_EQ(values[0], 1);
EXPECT_EQ(values[1], 2);
EXPECT_EQ(values[2], 3);
}
TEST(kmp_str_ref_test, ParseWithWhitespace) {
kmp_str_ref s(" hello world ");
s.skip_space();
kmp_str_ref word1 = s.take_while([](char c) { return c != ' '; });
s.drop_front(word1.length());
s.skip_space();
kmp_str_ref word2 = s.take_while([](char c) { return c != ' '; });
EXPECT_EQ(word1.length(), 5u);
EXPECT_TRUE(equals(word1, "hello"));
EXPECT_EQ(word2.length(), 5u);
EXPECT_TRUE(equals(word2, "world"));
}
} // namespace