Compare commits

...

5 Commits

Author SHA1 Message Date
ae7c841fff
tests: Make building tests optional, although with no option to toggle them on for now
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-28 20:02:49 +02:00
b34f2149ee
tests: Test libluna's UTF-8 suite 2023-04-28 20:01:45 +02:00
c5a867d81c
libluna: Add wcscmp 2023-04-28 20:00:26 +02:00
15dcd6ad15
libluna: Check the whole string in Utf8StringDecoder::code_points() 2023-04-28 20:00:14 +02:00
b4a5aff071
libluna: Fix UTF-8 encoding 2023-04-28 19:59:40 +02:00
5 changed files with 159 additions and 3 deletions

View File

@ -20,6 +20,7 @@ extern "C"
char* strtok(char* str, const char* delim); char* strtok(char* str, const char* delim);
usize wcslen(const wchar_t* str); usize wcslen(const wchar_t* str);
int wcscmp(const wchar_t* a, const wchar_t* b);
char* strdup(const char* str); char* strdup(const char* str);
char* strndup(const char* str, usize max); char* strndup(const char* str, usize max);

View File

@ -64,6 +64,16 @@ extern "C"
return *(const u8*)a - *(const u8*)b; return *(const u8*)a - *(const u8*)b;
} }
int wcscmp(const wchar_t* a, const wchar_t* b)
{
while (*a && (*a == *b))
{
a++;
b++;
}
return *(const u8*)a - *(const u8*)b;
}
int strncmp(const char* a, const char* b, usize max) int strncmp(const char* a, const char* b, usize max)
{ {
const char* s = a; const char* s = a;

View File

@ -35,6 +35,7 @@ static Result<bool> encode_wide_char_as_utf8(wchar_t c, char* result, usize& len
const usize utf8_len = TRY(wide_char_length_as_utf8(c)); const usize utf8_len = TRY(wide_char_length_as_utf8(c));
if (utf8_len > len) { return false; } // Not enough space if (utf8_len > len) { return false; } // Not enough space
len = utf8_len;
u8* buf = (u8*)result; u8* buf = (u8*)result;
@ -137,9 +138,9 @@ Result<usize> Utf8StringDecoder::code_points() const
while ((usize)(it - m_str) < m_byte_length) while ((usize)(it - m_str) < m_byte_length)
{ {
const usize utf8_len = TRY(utf8_char_length(*it)); usize mb_len = m_byte_length - (usize)(it - m_str); // Remaining space
if ((usize)(it - m_str) + utf8_len > m_byte_length) return err(EILSEQ); TRY(encode_utf8_as_wide_char(it, mb_len));
it += utf8_len; it += mb_len;
len++; len++;
} }

View File

@ -16,5 +16,8 @@ function(luna_test SOURCE_FILE APP_NAME SETUID)
endif() endif()
endfunction() endfunction()
if(BUILD_TESTS)
luna_test(libluna/TestVector.cpp TestVector OFF) luna_test(libluna/TestVector.cpp TestVector OFF)
luna_test(libluna/TestBase64.cpp TestBase64 OFF) luna_test(libluna/TestBase64.cpp TestBase64 OFF)
luna_test(libluna/TestUtf8.cpp TestUtf8 OFF)
endif()

141
tests/libluna/TestUtf8.cpp Normal file
View File

@ -0,0 +1,141 @@
#include <luna/CString.h>
#include <luna/Utf8.h>
#include <test.h>
TestResult test_basic_utf8_ascii_decoding()
{
wchar_t buf[20];
Utf8StringDecoder decoder("hello, world!");
validate(!decoder.decode(buf, sizeof(buf)).has_error());
validate(!wcscmp(buf, L"hello, world!"));
test_success;
}
TestResult test_basic_utf8_ascii_encoding()
{
char buf[20];
Utf8StringEncoder encoder(L"hello, world!");
validate(!encoder.encode(buf, sizeof(buf)).has_error());
validate(!strcmp(buf, "hello, world!"));
test_success;
}
TestResult test_utf8_decoding_with_non_ascii_chars()
{
wchar_t buf[20];
Utf8StringDecoder decoder("¿ñ?");
validate(!decoder.decode(buf, sizeof(buf)).has_error());
validate(!wcscmp(buf, L"¿ñ?"));
test_success;
}
TestResult test_utf8_encoding_with_non_ascii_chars()
{
char buf[20];
Utf8StringEncoder encoder(L"¿ñ?");
validate(!encoder.encode(buf, sizeof(buf)).has_error());
validate(!strcmp(buf, "¿ñ?"));
test_success;
}
TestResult test_utf8_decoding_invalid_continuation_byte_at_start()
{
Utf8StringDecoder decoder("\x83");
validate(decoder.code_points().has_error());
test_success;
}
TestResult test_utf8_decoding_invalid_start_byte_at_continuation()
{
Utf8StringDecoder decoder("\xc2\x7f");
validate(decoder.code_points().has_error());
test_success;
}
TestResult test_utf8_decoding_unfinished_sequence()
{
Utf8StringDecoder decoder("\xe0\x83");
validate(decoder.code_points().has_error());
test_success;
}
TestResult test_utf8_decoding_invalid_bytes()
{
Utf8StringDecoder decoder("\xf5\xf7");
validate(decoder.code_points().has_error());
test_success;
}
TestResult test_utf8_decoding_overlong_encoding()
{
Utf8StringDecoder decoder("\xe0\x80\x80");
validate(decoder.code_points().has_error());
test_success;
}
TestResult test_utf8_decoder_calculate_code_points()
{
Utf8StringDecoder decoder("¿ñ? ✨");
auto rc = decoder.code_points();
validate(!rc.has_error());
validate(rc.value() == 5);
test_success;
}
TestResult test_utf8_encoder_calculate_byte_length()
{
Utf8StringEncoder encoder(L"¿ñ? ✨");
auto rc = encoder.byte_length();
validate(!rc.has_error());
validate(rc.value() == 9);
test_success;
}
TestResult test_utf8_encoder_code_points_outside_unicode()
{
const wchar_t buf[] = { 0x120000, 0x000000 };
Utf8StringEncoder encoder(buf);
validate(encoder.byte_length().has_error());
test_success;
}
Result<void> test_main()
{
test_prelude;
run_test(test_basic_utf8_ascii_decoding);
run_test(test_basic_utf8_ascii_encoding);
run_test(test_utf8_decoding_with_non_ascii_chars);
run_test(test_utf8_encoding_with_non_ascii_chars);
run_test(test_utf8_decoding_invalid_continuation_byte_at_start);
run_test(test_utf8_decoding_invalid_start_byte_at_continuation);
run_test(test_utf8_decoding_unfinished_sequence);
run_test(test_utf8_decoding_invalid_bytes);
run_test(test_utf8_decoding_overlong_encoding);
run_test(test_utf8_decoder_calculate_code_points);
run_test(test_utf8_encoder_calculate_byte_length);
run_test(test_utf8_encoder_code_points_outside_unicode);
return {};
}