/* * Copyright 2020 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "args.hh" #include "command.hh" #include "dispatch_table.hh" uint64_t test_total_written; std::string test_host_file_name; std::vector test_written_data; uint64_t test_max_buf_size; uint64_t test_m5_write_file(void *buffer, uint64_t len, uint64_t offset, const char *filename) { if (test_max_buf_size && len > test_max_buf_size) len = test_max_buf_size; test_total_written += len; if (test_host_file_name == "") test_host_file_name = filename; else EXPECT_EQ(test_host_file_name, filename); if (offset == 0) test_written_data.clear(); size_t required_size = offset + len; if (test_written_data.size() < required_size) test_written_data.resize(required_size); memcpy(test_written_data.data() + offset, buffer, len); return len; } DispatchTable dt = { .m5_write_file = &test_m5_write_file }; std::string cout_output; bool run(std::initializer_list arg_args) { test_total_written = 0; test_host_file_name = ""; test_written_data.clear(); Args args(arg_args); // Redirect cout into a stringstream. std::stringstream buffer; std::streambuf *orig = std::cout.rdbuf(buffer.rdbuf()); bool res = Command::run(dt, args); // Capture the contents of the stringstream and restore cout. cout_output = buffer.str(); std::cout.rdbuf(orig); return res; } class TempFile { private: size_t _size; int fd; std::string _path; void *_buf; public: TempFile(size_t _size) : _size(_size) { // Generate a temporary filename. char *tmp_name = strdup("/tmp/writefile.test.XXXXXXXX"); fd = mkstemp(tmp_name); _path = tmp_name; free(tmp_name); // Make the file the appropriate length. assert(!ftruncate(fd, _size)); // mmap the file. _buf = mmap(nullptr, _size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); assert(_buf); // Fill it with an incrementing 32 bit integers. int chunk_size = sizeof(uint32_t); size_t num_chunks = _size / chunk_size; int leftovers = _size % chunk_size; uint32_t *buf32 = (uint32_t *)_buf; uint32_t val = 0; for (size_t i = 0; i < num_chunks; i++) *buf32++ = val++; if (leftovers) memcpy(buf32, &val, leftovers); // Make sure our new contents are out there. msync(_buf, _size, MS_SYNC | MS_INVALIDATE); }; ~TempFile() { unlink(path().c_str()); close(fd); } size_t size() const { return _size; } const std::string &path() const { return _path; } const void *buf() const { return _buf; } void verify() { verify(path()); } void verify(const std::string &expected_path) { EXPECT_EQ(test_written_data.size(), size()); EXPECT_EQ(memcmp(test_written_data.data(), buf(), size()), 0); EXPECT_EQ(test_host_file_name, expected_path); std::ostringstream os; os << "Opening \"" << path() << "\"."; EXPECT_THAT(cout_output, ::testing::HasSubstr(os.str())); os.str(""); os << "Wrote " << size() << " bytes."; EXPECT_THAT(cout_output, ::testing::HasSubstr(os.str())); } }; TEST(Writefile, NoArguments) { // Call with no arguments. EXPECT_FALSE(run({"writefile"})); EXPECT_EQ(test_total_written, 0); } TEST(Writefile, ThreeArguments) { // Call with no arguments. EXPECT_FALSE(run({"writefile", "1", "2", "3"})); EXPECT_EQ(test_total_written, 0); } TEST(Writefile, SmallFile) { // Write a small file. TempFile tmp(16); test_max_buf_size = 0; EXPECT_TRUE(run({"writefile", tmp.path()})); tmp.verify(); } TEST(Writefile, SmallFileHostName) { // Write a small file with a different host file name. TempFile tmp(16); test_max_buf_size = 0; std::string host_path = "/different/host/path"; EXPECT_TRUE(run({"writefile", tmp.path(), host_path})); tmp.verify(host_path); } TEST(Writefile, MultipleChunks) { // Write a file which will need to be split into multiple whole chunks. TempFile tmp(256 * 1024 * 4); test_max_buf_size = 0; EXPECT_TRUE(run({"writefile", tmp.path()})); tmp.verify(); } TEST(Writefile, MultipleAndPartialChunks) { // Write a file which will be split into some whole and one partial chunk. TempFile tmp(256 * 1024 * 2 + 256); test_max_buf_size = 0; EXPECT_TRUE(run({"writefile", tmp.path()})); tmp.verify(); } TEST(Writefile, OddSizedChunks) { // Write a file in chunks that aren't nicely aligned. TempFile tmp(256 * 1024); test_max_buf_size = 13; EXPECT_TRUE(run({"writefile", tmp.path()})); tmp.verify(); } TEST(Writefile, CappedWriteSize) { // Write a file, accepting less than the requested amount of data. TempFile tmp(256 * 1024 * 2 + 256); test_max_buf_size = 256; EXPECT_TRUE(run({"writefile", tmp.path()})); tmp.verify(); } TEST(WritefileDeathTest, BadFile) { EXPECT_EXIT(run({"writefile", "this is not a valid path#$#$://\\\\"}), ::testing::ExitedWithCode(2), "Error opening "); }