mirror of
https://github.com/SerenityOS/serenity
synced 2024-07-22 02:26:11 +00:00
Utilities: Allow uniq to work on lines of arbitrary length
Calls to `read_line` are replaced with `read_line_with_resize` and `swap`s of StringViews, which assume a consistent location of the underlying ByteBuffers, are replaced. A test file has been added for uniq, which includes a test case for long lines.
This commit is contained in:
parent
22705e3065
commit
82887473d2
|
@ -1,6 +1,7 @@
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
TestSed.cpp
|
TestSed.cpp
|
||||||
TestPatch.cpp
|
TestPatch.cpp
|
||||||
|
TestUniq.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(source IN LISTS TEST_SOURCES)
|
foreach(source IN LISTS TEST_SOURCES)
|
||||||
|
|
52
Tests/Utilities/TestUniq.cpp
Normal file
52
Tests/Utilities/TestUniq.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Rodrigo Tobar <rtobarc@gmail.com>.
|
||||||
|
* Copyright (c) 2024, Daniel Gaston <tfd@tuta.io>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
|
#include <LibCore/Command.h>
|
||||||
|
#include <LibCore/File.h>
|
||||||
|
#include <LibTest/Macros.h>
|
||||||
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
|
static void run_uniq(Vector<char const*>&& arguments, StringView standard_input, StringView expected_stdout)
|
||||||
|
{
|
||||||
|
MUST(arguments.try_insert(0, "uniq"));
|
||||||
|
MUST(arguments.try_append(nullptr));
|
||||||
|
auto uniq = MUST(Core::Command::create("uniq"sv, arguments.data()));
|
||||||
|
MUST(uniq->write(standard_input));
|
||||||
|
auto [stdout, stderr] = MUST(uniq->read_all());
|
||||||
|
auto status = MUST(uniq->status());
|
||||||
|
if (status != Core::Command::ProcessResult::DoneWithZeroExitCode) {
|
||||||
|
FAIL(ByteString::formatted("uniq didn't exit cleanly: status: {}, stdout: {}, stderr: {}", static_cast<int>(status), StringView { stdout.bytes() }, StringView { stderr.bytes() }));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(StringView { expected_stdout.bytes() }, StringView { stdout.bytes() });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(two_duplicate_lines)
|
||||||
|
{
|
||||||
|
run_uniq({}, "AAA\nAAA\n"sv, "AAA\n"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(two_unique_lines)
|
||||||
|
{
|
||||||
|
run_uniq({}, "AAA\nAaA\n"sv, "AAA\nAaA\n"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(long_line)
|
||||||
|
{
|
||||||
|
auto input = Array<u8, 4096> {};
|
||||||
|
auto expected_output = Array<u8, 2048> {};
|
||||||
|
// Create two lines of 2047 A's and a newline.
|
||||||
|
input.fill('A');
|
||||||
|
input[2047] = '\n';
|
||||||
|
input[4095] = '\n';
|
||||||
|
|
||||||
|
expected_output.fill('A');
|
||||||
|
expected_output[2047] = '\n';
|
||||||
|
|
||||||
|
run_uniq({}, StringView { input }, StringView { expected_output });
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Matthew L. Curry <matthew.curry@gmail.com>
|
* Copyright (c) 2020, Matthew L. Curry <matthew.curry@gmail.com>
|
||||||
|
* Copyright (c) 2024, Daniel Gaston <tfd@tuta.io>.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -86,14 +87,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
ByteBuffer previous_buf = TRY(ByteBuffer::create_uninitialized(1024));
|
ByteBuffer previous_buf = TRY(ByteBuffer::create_uninitialized(1024));
|
||||||
ByteBuffer current_buf = TRY(ByteBuffer::create_uninitialized(1024));
|
ByteBuffer current_buf = TRY(ByteBuffer::create_uninitialized(1024));
|
||||||
|
|
||||||
StringView previous = TRY(infile->read_line(previous_buf));
|
StringView previous = TRY(infile->read_line_with_resize(previous_buf));
|
||||||
StringView previous_to_compare = skip(previous, skip_chars, skip_fields);
|
StringView previous_to_compare = skip(previous, skip_chars, skip_fields);
|
||||||
|
|
||||||
while (TRY(infile->can_read_line())) {
|
while (TRY(infile->can_read_line())) {
|
||||||
// FIXME: The buffer does not automatically resize,
|
|
||||||
// and this will return EMSGSIZE if the read line
|
StringView current = TRY(infile->read_line_with_resize(current_buf));
|
||||||
// is more than 1024 bytes.
|
|
||||||
StringView current = TRY(infile->read_line(current_buf));
|
|
||||||
|
|
||||||
StringView current_to_compare = skip(current, skip_chars, skip_fields);
|
StringView current_to_compare = skip(current, skip_chars, skip_fields);
|
||||||
bool lines_equal = ignore_case ? current_to_compare.equals_ignoring_ascii_case(previous_to_compare) : current_to_compare == previous_to_compare;
|
bool lines_equal = ignore_case ? current_to_compare.equals_ignoring_ascii_case(previous_to_compare) : current_to_compare == previous_to_compare;
|
||||||
|
@ -103,9 +102,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
} else {
|
} else {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
swap(current_to_compare, previous_to_compare);
|
|
||||||
swap(current_buf, previous_buf);
|
swap(current_buf, previous_buf);
|
||||||
swap(current, previous);
|
// The StringViews cannot be swapped since read_line_with_resize
|
||||||
|
// potentially changes the location of the buffers due to reallocation.
|
||||||
|
// Instead create a new StringView of what was most recently read in.
|
||||||
|
previous = StringView { previous_buf.span().trim(current.length()) };
|
||||||
|
previous_to_compare = skip(previous, skip_chars, skip_fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(write_line_content(previous, count, duplicates_only, print_count, *outfile));
|
TRY(write_line_content(previous, count, duplicates_only, print_count, *outfile));
|
||||||
|
|
Loading…
Reference in a new issue