/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ #define ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_ #include <errno.h> #include <memory> #include <string> #include "common_runtime_test.h" namespace unix_file { class RandomAccessFileTest : public testing::Test { protected: virtual ~RandomAccessFileTest() { } // Override this to return an instance of the subclass under test that's // backed by a temporary file. virtual RandomAccessFile* MakeTestFile() = 0; virtual void SetUp() { art::CommonRuntimeTest::SetUpAndroidData(android_data_); } virtual void TearDown() { art::CommonRuntimeTest::TearDownAndroidData(android_data_, true); } std::string GetTmpPath(const std::string& name) { std::string path; path = android_data_; path += "/"; path += name; return path; } // TODO(enh): ReadString (and WriteString) might be generally useful. static bool ReadString(RandomAccessFile* f, std::string* s) { s->clear(); char buf[256]; int64_t n = 0; int64_t offset = 0; while ((n = f->Read(buf, sizeof(buf), offset)) > 0) { s->append(buf, n); offset += n; } return n != -1; } void TestRead() { char buf[256]; std::unique_ptr<RandomAccessFile> file(MakeTestFile()); // Reading from the start of an empty file gets you zero bytes, however many // you ask for. ASSERT_EQ(0, file->Read(buf, 0, 0)); ASSERT_EQ(0, file->Read(buf, 123, 0)); const std::string content("hello"); ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), 0))); TestReadContent(content, file.get()); CleanUp(file.get()); } void TestReadContent(const std::string& content, RandomAccessFile* file) { const int buf_size = content.size() + 10; std::unique_ptr<char> buf(new char[buf_size]); // Can't read from a negative offset. ASSERT_EQ(-EINVAL, file->Read(buf.get(), 0, -123)); // Reading too much gets us just what's in the file. ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Read(buf.get(), buf_size, 0))); ASSERT_EQ(std::string(buf.get(), content.size()), content); // We only get as much as we ask for. const size_t short_request = 2; ASSERT_LT(short_request, content.size()); ASSERT_EQ(short_request, static_cast<uint64_t>(file->Read(buf.get(), short_request, 0))); ASSERT_EQ(std::string(buf.get(), short_request), content.substr(0, short_request)); // We don't have to start at the beginning. const int non_zero_offset = 2; ASSERT_GT(non_zero_offset, 0); ASSERT_EQ(short_request, static_cast<uint64_t>(file->Read(buf.get(), short_request, non_zero_offset))); ASSERT_EQ(std::string(buf.get(), short_request), content.substr(non_zero_offset, short_request)); // Reading past the end gets us nothing. ASSERT_EQ(0, file->Read(buf.get(), buf_size, file->GetLength())); ASSERT_EQ(0, file->Read(buf.get(), buf_size, file->GetLength() + 1)); } void TestSetLength() { const std::string content("hello"); std::unique_ptr<RandomAccessFile> file(MakeTestFile()); ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), 0))); ASSERT_EQ(content.size(), static_cast<uint64_t>(file->GetLength())); // Can't give a file a negative length. ASSERT_EQ(-EINVAL, file->SetLength(-123)); // Can truncate the file. int64_t new_length = 2; ASSERT_EQ(0, file->SetLength(new_length)); ASSERT_EQ(new_length, file->GetLength()); std::string new_content; ASSERT_TRUE(ReadString(file.get(), &new_content)); ASSERT_EQ(content.substr(0, 2), new_content); // Expanding the file appends zero bytes. new_length = file->GetLength() + 1; ASSERT_EQ(0, file->SetLength(new_length)); ASSERT_EQ(new_length, file->GetLength()); ASSERT_TRUE(ReadString(file.get(), &new_content)); ASSERT_EQ('\0', new_content[new_length - 1]); CleanUp(file.get()); } void TestWrite() { const std::string content("hello"); std::unique_ptr<RandomAccessFile> file(MakeTestFile()); // Can't write to a negative offset. ASSERT_EQ(-EINVAL, file->Write(content.data(), 0, -123)); // Writing zero bytes of data is a no-op. ASSERT_EQ(0, file->Write(content.data(), 0, 0)); ASSERT_EQ(0, file->GetLength()); // We can write data. ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), 0))); ASSERT_EQ(content.size(), static_cast<uint64_t>(file->GetLength())); std::string new_content; ASSERT_TRUE(ReadString(file.get(), &new_content)); ASSERT_EQ(new_content, content); // We can read it back. char buf[256]; ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Read(buf, sizeof(buf), 0))); ASSERT_EQ(std::string(buf, content.size()), content); // We can append data past the end. ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), file->GetLength() + 1))); int64_t new_length = 2*content.size() + 1; ASSERT_EQ(file->GetLength(), new_length); ASSERT_TRUE(ReadString(file.get(), &new_content)); ASSERT_EQ(std::string("hello\0hello", new_length), new_content); CleanUp(file.get()); } virtual void CleanUp(RandomAccessFile* file ATTRIBUTE_UNUSED) { } protected: std::string android_data_; }; } // namespace unix_file #endif // ART_RUNTIME_BASE_UNIX_FILE_RANDOM_ACCESS_FILE_TEST_H_