diff --git a/.gitignore b/.gitignore index 1f459def..d54475c2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ /src/tests/proctests /src/tests/loadtests /src/tests/envtests +/src/tests/iostreamtests /src/tests/cptests/cptests /src/tests/cptests/corpus /src/tests/cptests/fuzz diff --git a/src/tests/Makefile b/src/tests/Makefile index 8782f994..51fd9805 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -3,12 +3,12 @@ ALL_TEST_CXXFLAGS=$(CPPFLAGS) $(TEST_CXXFLAGS) $(TEST_CXXFLAGS_EXTRA) ALL_TEST_LDFLAGS=$(TEST_LDFLAGS) $(TEST_LDFLAGS_EXTRA) -objects = tests.o test-dinit.o proctests.o loadtests.o envtests.o test-run-child-proc.o test-bpsys.o +objects = tests.o test-dinit.o proctests.o loadtests.o envtests.o iostreamtests.o test-run-child-proc.o test-bpsys.o parent_objs = service.o proc-service.o dinit-log.o load-service.o baseproc-service.o dinit-env.o control.o check: build-tests run-tests -build-tests: tests proctests loadtests envtests +build-tests: tests proctests loadtests envtests iostreamtests $(MAKE) -C cptests build-tests run-tests: build-tests @@ -16,6 +16,7 @@ run-tests: build-tests ./proctests ./loadtests ./envtests + ./iostreamtests $(MAKE) -C cptests run-tests tests: $(parent_objs) tests.o test-dinit.o test-bpsys.o test-run-child-proc.o @@ -23,13 +24,21 @@ tests: $(parent_objs) tests.o test-dinit.o test-bpsys.o test-run-child-proc.o proctests: $(parent_objs) proctests.o test-dinit.o test-bpsys.o test-run-child-proc.o $(CXX) -o proctests $(parent_objs) proctests.o test-dinit.o test-bpsys.o test-run-child-proc.o $(ALL_TEST_LDFLAGS) - + loadtests: $(parent_objs) loadtests.o test-dinit.o test-bpsys.o test-run-child-proc.o $(CXX) -o loadtests $(parent_objs) loadtests.o test-dinit.o test-bpsys.o test-run-child-proc.o $(ALL_TEST_LDFLAGS) envtests: $(parent_objs) envtests.o test-dinit.o test-bpsys.o test-run-child-proc.o $(CXX) -o envtests $(parent_objs) envtests.o test-dinit.o test-bpsys.o test-run-child-proc.o $(ALL_TEST_LDFLAGS) +# dinit-iostream contains global objects which require special handling in test environments. +# Therefore, avoid linking it to tests by default (instead include it explicitly in relevant tests eg iostreamtests). +dinit-iostream.o: ../dinit-iostream.cc + $(CXX) $(ALL_TEST_CXXFLAGS) -MMD -MP -Itest-includes -I../../dasynq/include -I../../build/includes -I../includes -c $< -o $@ + +iostreamtests: iostreamtests.o dinit-iostream.o test-bpsys.o + $(CXX) -o iostreamtests iostreamtests.o dinit-iostream.o test-bpsys.o $(ALL_TEST_LDFLAGS) + $(objects): %.o: %.cc $(CXX) $(ALL_TEST_CXXFLAGS) -MMD -MP -Itest-includes -I../../dasynq/include -I../../build/includes -I../includes -c $< -o $@ @@ -38,7 +47,7 @@ $(parent_objs): %.o: ../%.cc clean: $(MAKE) -C cptests clean - rm -f *.o *.d tests proctests loadtests envtests + rm -f *.o *.d tests proctests loadtests envtests iostreamtests -include $(objects:.o=.d) -include $(parent_objs:.o=.d) diff --git a/src/tests/iostreamtests.cc b/src/tests/iostreamtests.cc new file mode 100644 index 00000000..bcecc52f --- /dev/null +++ b/src/tests/iostreamtests.cc @@ -0,0 +1,198 @@ +#include +#include +#include +#include + +#include "baseproc-sys.h" +#include "dinit-iostream.h" + +#ifdef NDEBUG +#error "This file must be built with assertions ENABLED!" +#endif + +void ostream_basic_test() +{ + int fd = bp_sys::allocfd(); + dio::ostream stream(fd); + dio::streambuf* buf = stream.get_buf(); + assert(buf != nullptr); + assert(stream.good()); + + const char msg[] = "This is a test message!\n"; + + assert(stream.write(msg)); + assert(buf->get_length() == sizeof(msg) - 1); + + char* ptr = buf->get_ptr(0); + unsigned len = buf->get_contiguous_length(ptr); + assert(strncmp(ptr, msg, len) == 0); + + assert(stream.flush_nx()); + assert(buf->get_length() == 0); + + std::vector wdata; + bp_sys::extract_written_data(fd, wdata); + + assert(wdata.size() == sizeof(msg) - 1); + assert(strncmp(wdata.data(), msg, wdata.size()) == 0); +} + +void ostream_write_buf_test() +{ + int fd = bp_sys::allocfd(); + dio::ostream stream(fd); + dio::streambuf* buf = stream.get_buf(); + assert(buf != nullptr); + assert(stream.good()); + + const char msg[] = "This is a test message!\n"; + + assert(stream.write_buf(msg) == sizeof(msg) - 1); + assert(buf->get_length() == sizeof(msg) - 1); + + char* ptr = buf->get_ptr(0); + unsigned len = buf->get_contiguous_length(ptr); + assert(strncmp(ptr, msg, len) == 0); + + assert(stream.flush_nx()); + assert(buf->get_length() == 0); + + std::vector wdata; + bp_sys::extract_written_data(fd, wdata); + + assert(wdata.size() == sizeof(msg) - 1); + assert(strncmp(wdata.data(), msg, wdata.size()) == 0); +} + +void ostream_int_conversion_test() +{ + int fd = bp_sys::allocfd(); + dio::ostream stream(fd); + dio::streambuf *buf = stream.get_buf(); + assert(buf != nullptr); + assert(stream.good()); + + const char msg1[] = "The 2 + 2 equals to: "; + const int msg2 = 4; + const char msg_full[] = "The 2 + 2 equals to: 4"; + + assert(stream.write(msg1, msg2)); + assert(buf->get_length() == sizeof(msg_full) - 1); + + char* ptr = buf->get_ptr(0); + unsigned len = buf->get_contiguous_length(ptr); + assert(strncmp(ptr, msg_full, len) == 0); + + assert(stream.flush_nx()); + assert(buf->get_length() == 0); + + std::vector wdata; + bp_sys::extract_written_data(fd, wdata); + + assert(wdata.size() == sizeof(msg_full) - 1); + assert(strncmp(wdata.data(), msg_full, wdata.size()) == 0); +} + +void ostream_large_msg_test() +{ + int fd = bp_sys::allocfd(); + dio::ostream stream(fd); + dio::streambuf *buf = stream.get_buf(); + assert(buf != nullptr); + assert(stream.good()); + + char msg[IOSTREAM_BUFSIZE + 2]; + std::fill_n(msg, IOSTREAM_BUFSIZE + 1, 'a'); + msg[IOSTREAM_BUFSIZE + 1] = '\0'; + + assert(stream.write(msg)); + assert(buf->get_length() == 1); + assert(stream.flush()); + + std::vector wdata; + bp_sys::extract_written_data(fd, wdata); + + assert(wdata.size() == sizeof(msg) - 1); + assert(strncmp(wdata.data(), msg, wdata.size()) == 0); +} + +void istream_basic_test() +{ + std::vector wdata = { 'L', '1', '\n', 'L','2', '\n', '\n', 'L', '3' }; + bp_sys::supply_file_content("file", std::move(wdata)); + + dio::istream stream; + dio::streambuf *buf = stream.get_buf(); + assert(buf != nullptr); + assert(stream.good()); + + assert(stream.open_nx("file")); + assert(stream.is_open()); + assert(stream.get_fd() >= 0); + + std::string line; + + assert(dio::get_line(stream, line) == 2); + assert(line.compare("L1") == 0); + + assert(dio::get_line(stream, line) == 2); + assert(line.compare("L2") == 0); + + assert(dio::get_line(stream, line) == 1); + assert(line.size() == 0); + + assert(dio::get_line(stream, line) == 2); + assert(line.compare("L3") == 0); + + assert(dio::get_line_until_eof(stream, line) == 0); + assert(stream.eof()); + assert(line.compare("L3") == 0); + + assert(stream.close()); +} + +void istream_buffer_boundary_test() +{ + int fd = bp_sys::allocfd(); + dio::istream stream(fd); + dio::streambuf *buf = stream.get_buf(); + assert(buf != nullptr); + assert(stream.good()); + + std::array msg; + msg.fill('a'); + buf->append(msg.begin(), msg.size()); + + std::fill_n(msg.begin(), 100, 'b'); + buf->consume(100); + buf->append(msg.begin(), 100); + + std::string line; + assert(dio::get_line(stream, line) == IOSTREAM_BUFSIZE); + assert(strncmp(line.c_str(), msg.begin(), IOSTREAM_BUFSIZE)); +} + +#define RUN_TEST(name, spacing) \ + std::cout << #name "..." spacing << std::flush; \ + name(); \ + std::cout << "PASSED" << std::endl; + +int main(int argc, char **argv) +{ + RUN_TEST(ostream_basic_test, " "); + RUN_TEST(ostream_write_buf_test, " "); + RUN_TEST(ostream_int_conversion_test, " "); + RUN_TEST(ostream_large_msg_test, " "); + RUN_TEST(istream_basic_test, " "); + RUN_TEST(istream_buffer_boundary_test, " "); + + // The destructors of the cout, cerr, and cin global objects will attempt to close the associated + // file descriptors, if they are still open. The bp_sys test infrastructure may have been torn + // down by that time, however, leading to errors. So, close the (bp_sys-based) descriptors now + // to avoid that. + dio::cout.close_nx(); + dio::cerr.close_nx(); + dio::cin.close_nx(); + + return 0; +} diff --git a/src/tests/meson.build b/src/tests/meson.build index aa2f79d9..d40c0956 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -52,7 +52,15 @@ envtests_exec = executable( for_tests_dinit_sources, include_directories: for_tests_incdir ) +iostreamtests_exec = executable( + 'iostreamtests', + 'iostreamtests.cc', + '../dinit-iostream.cc', + 'test-bpsys.cc', + include_directories: for_tests_incdir +) test('tests', tests_exec, suite: 'unit_tests') test('proctests', proctests_exec, suite: 'unit_tests') test('loadtests', loadtests_exec, workdir: meson.current_source_dir(), suite: 'unit_tests') test('envtests', envtests_exec, suite: 'unit_tests') +test('iostreamtests', iostreamtests_exec, suite: 'unit_tests')