Files
Shannon Booth 6a6259c729 tests: declare compatibility per test
GNU comparison was controlled per test executable. That kept process
tests in test_unit from being compared, while enabling comparison there
would also run its unit tests twice.

Introduce COMPAT_TEST for cases that run against both sb_patch and GNU
patch. Keep PATCH_TEST for project-specific behavior and TEST for unit
coverage. Mark stable GNU differences as expected compatibility failures.
Keep host-dependent ed behavior project-only because GNU delegates those
scripts to an external editor.

Merge the normal and conditional PTY tests into one patch_tests runner
now that executable boundaries no longer control GNU comparison. Keep
test_oom separate because it runs patch_oom.
2026-07-25 09:53:53 +02:00

264 lines
7.2 KiB
C++

// SPDX-License-Identifier: BSD-3-Clause
// Copyright 2022-2023 Shannon Booth <shannon.ml.booth@gmail.com>
#include <ios>
#include <patch/file.h>
#include <patch/system.h>
#include <patch/test.h>
#include <system_error>
TEST(file_get_line_lf)
{
Patch::File patch_file = Patch::File::create_temporary_with_content(
"first line\n"
"second line\n"
"last line, trailing newline\n");
Patch::NewLine newline;
std::string line;
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::LF);
EXPECT_EQ(line, "first line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::LF);
EXPECT_EQ(line, "second line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::LF);
EXPECT_EQ(line, "last line, trailing newline");
EXPECT_FALSE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "");
}
TEST(file_lf_missing_at_end_of_file)
{
Patch::File patch_file = Patch::File::create_temporary_with_content(
"first line\n"
"second line\n"
"last line, no trailing newline");
Patch::NewLine newline;
std::string line;
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::LF);
EXPECT_EQ(line, "first line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::LF);
EXPECT_EQ(line, "second line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "last line, no trailing newline");
EXPECT_FALSE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "");
}
TEST(file_new_line_crlf)
{
Patch::File patch_file = Patch::File::create_temporary_with_content(
"first line\r\n"
"second line\r\n"
"last line, trailing newline\r\n");
Patch::NewLine newline;
std::string line;
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::CRLF);
EXPECT_EQ(line, "first line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::CRLF);
EXPECT_EQ(line, "second line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::CRLF);
EXPECT_EQ(line, "last line, trailing newline");
EXPECT_FALSE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "");
}
TEST(file_crlf_missing_at_end_of_file)
{
Patch::File patch_file = Patch::File::create_temporary_with_content(
"first line\r\n"
"second line\r\n"
"last line, missing newline");
Patch::NewLine newline;
std::string line;
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::CRLF);
EXPECT_EQ(line, "first line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::CRLF);
EXPECT_EQ(line, "second line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "last line, missing newline");
EXPECT_FALSE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "");
}
TEST(file_mixed_new_lines)
{
Patch::File patch_file = Patch::File::create_temporary_with_content(
"lf line\n"
"crlf line\r\n"
"missing newline");
Patch::NewLine newline;
std::string line;
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::LF);
EXPECT_EQ(line, "lf line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::CRLF);
EXPECT_EQ(line, "crlf line");
EXPECT_TRUE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "missing newline");
EXPECT_FALSE(patch_file.get_line(line, &newline));
EXPECT_EQ(newline, Patch::NewLine::None);
EXPECT_EQ(line, "");
}
TEST(file_move_construct_move_assign)
{
// Construct a temporary file, get_line until eof and bad.
Patch::File file_orig = Patch::File::create_temporary_with_content("abc\n");
EXPECT_FALSE(file_orig.eof());
EXPECT_TRUE(file_orig);
std::string content;
EXPECT_TRUE(file_orig.get_line(content));
EXPECT_EQ(content, "abc");
EXPECT_FALSE(file_orig.eof());
EXPECT_TRUE(file_orig);
EXPECT_FALSE(file_orig.get_line(content));
EXPECT_EQ(content, "");
EXPECT_TRUE(file_orig.eof());
EXPECT_TRUE(file_orig);
EXPECT_FALSE(file_orig.get_line(content));
EXPECT_EQ(content, "");
EXPECT_TRUE(file_orig.eof());
EXPECT_FALSE(file_orig);
// Move construct a new file, it should inherit state.
Patch::File file_move_constructed = std::move(file_orig);
EXPECT_TRUE(file_move_constructed.eof());
EXPECT_FALSE(file_move_constructed);
// Move assign a new file, it should inherit state.
Patch::File file_move_assigned = Patch::File::create_temporary_with_content("some stuff");
file_move_assigned = std::move(file_move_constructed);
EXPECT_TRUE(file_move_assigned.eof());
EXPECT_FALSE(file_move_assigned);
// Should still be able to read from the file, but using new content!
EXPECT_EQ(file_move_assigned.read_all_as_string(), "abc\n");
}
TEST(file_reopen_after_eof)
{
{
Patch::File file("first-file", std::ios_base::out);
file << "first\n";
}
{
Patch::File file("second-file", std::ios_base::out);
file << "second\n";
}
Patch::File file("first-file", std::ios_base::in);
std::string line;
EXPECT_TRUE(file.get_line(line));
EXPECT_EQ(line, "first");
EXPECT_FALSE(file.get_line(line));
EXPECT_TRUE(file.eof());
EXPECT_FALSE(file.get_line(line));
EXPECT_FALSE(file);
EXPECT_TRUE(file.open("second-file", std::ios_base::in));
EXPECT_FALSE(file.eof());
EXPECT_TRUE(file);
EXPECT_TRUE(file.get_line(line));
EXPECT_EQ(line, "second");
}
TEST(file_failed_reopen_preserves_stream)
{
{
Patch::File file("existing-file", std::ios_base::out);
file << "first\nsecond\n";
}
Patch::File file("existing-file", std::ios_base::in);
std::string line;
EXPECT_TRUE(file.get_line(line));
EXPECT_EQ(line, "first");
EXPECT_FALSE(file.open("file-that-does-not-exist", std::ios_base::in));
EXPECT_TRUE(file);
EXPECT_FALSE(file.eof());
EXPECT_TRUE(file.get_line(line));
EXPECT_EQ(line, "second");
}
TEST(file_reopen_closes_existing_file)
{
Patch::File file("first-file", std::ios_base::out);
file << "first";
EXPECT_TRUE(file.open("second-file", std::ios_base::out));
file << "second";
EXPECT_FILE_EQ("first-file", "first");
}
TEST(file_append)
{
{
Patch::File file("some-file", std::ios_base::out);
file << "first line!\n";
}
{
Patch::File file("some-file", std::ios_base::out | std::ios_base::app);
file << "second line!\n";
}
EXPECT_FILE_EQ("some-file", "first line!\nsecond line!\n");
}
TEST(file_open_failure)
{
EXPECT_THROW(Patch::File("file-that-does-not-exist"), std::system_error);
}