diff --git a/Tests/Utilities/CMakeLists.txt b/Tests/Utilities/CMakeLists.txt index 3baad39956..ddb18dc76a 100644 --- a/Tests/Utilities/CMakeLists.txt +++ b/Tests/Utilities/CMakeLists.txt @@ -1,6 +1,7 @@ set(TEST_SOURCES TestSed.cpp TestPatch.cpp + TestUniq.cpp ) foreach(source IN LISTS TEST_SOURCES) diff --git a/Tests/Utilities/TestUniq.cpp b/Tests/Utilities/TestUniq.cpp new file mode 100644 index 0000000000..2da57cd73d --- /dev/null +++ b/Tests/Utilities/TestUniq.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, Rodrigo Tobar . + * Copyright (c) 2024, Daniel Gaston . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +static void run_uniq(Vector&& 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(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 {}; + auto expected_output = Array {}; + // 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 }); +} diff --git a/Userland/Utilities/uniq.cpp b/Userland/Utilities/uniq.cpp index 59e9a3e5c0..5591d74501 100644 --- a/Userland/Utilities/uniq.cpp +++ b/Userland/Utilities/uniq.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Matthew L. Curry + * Copyright (c) 2024, Daniel Gaston . * * SPDX-License-Identifier: BSD-2-Clause */ @@ -86,14 +87,12 @@ ErrorOr serenity_main(Main::Arguments arguments) ByteBuffer previous_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); while (TRY(infile->can_read_line())) { - // FIXME: The buffer does not automatically resize, - // and this will return EMSGSIZE if the read line - // is more than 1024 bytes. - StringView current = TRY(infile->read_line(current_buf)); + + StringView current = TRY(infile->read_line_with_resize(current_buf)); 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; @@ -103,9 +102,13 @@ ErrorOr serenity_main(Main::Arguments arguments) } else { count++; } - swap(current_to_compare, previous_to_compare); + 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));