Chromium Code Reviews| Index: webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc |
| diff --git a/webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc b/webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7300f77c6f668dbe41a62b88b7ff203b006eb238 |
| --- /dev/null |
| +++ b/webkit/fileapi/media/picasa/pmp_column_reader_unittest.cc |
| @@ -0,0 +1,239 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <algorithm> |
| +#include <vector> |
| + |
| +#include "base/file_util.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "webkit/fileapi/media/picasa/pmp_column_reader.h" |
| +#include "webkit/fileapi/media/picasa/pmp_constants.h" |
| + |
| +namespace { |
| + |
| +using fileapi::PmpColumnReader; |
| + |
| +// Return a vector so we don't have to worry about memory management. |
| +std::vector<uint8> MakeHeader(const uint16 field_type, const uint32 row_count) { |
|
vandebo (ex-Chrome)
2013/03/28 00:56:04
All these functions might be better organized in a
tommycli
2013/03/29 00:16:01
Moving these to PmpTestUtil so I can use in multip
|
| + std::vector<uint8> header(fileapi::kPmpHeaderSize); |
| + |
| + // Copy in magic bytes. |
| + memcpy(&header[fileapi::kPmpMagic1Offset], &fileapi::kPmpMagic1, |
| + sizeof(fileapi::kPmpMagic1)); |
| + memcpy(&header[fileapi::kPmpMagic2Offset], &fileapi::kPmpMagic2, |
| + sizeof(fileapi::kPmpMagic2)); |
| + memcpy(&header[fileapi::kPmpMagic3Offset], &fileapi::kPmpMagic3, |
| + sizeof(fileapi::kPmpMagic3)); |
| + memcpy(&header[fileapi::kPmpMagic4Offset], &fileapi::kPmpMagic4, |
| + sizeof(fileapi::kPmpMagic4)); |
| + |
| + // Copy in field type. |
| + memcpy(&header[fileapi::kPmpFieldType1Offset], &field_type, 2); |
| + memcpy(&header[fileapi::kPmpFieldType2Offset], &field_type, 2); |
| + |
| + // Copy in row count. |
| + memcpy(&header[fileapi::kPmpRowCountOffset], &row_count, 4); |
| + |
| + return header; |
| +} |
| + |
| +// Flatten a vector of elements into an array of bytes. |
| +template<class T> |
| +std::vector<uint8> Flatten(const std::vector<T>& elems) { |
| + const uint8* elems0 = reinterpret_cast<const uint8*>(&elems[0]); |
| + std::vector<uint8> data_body(elems0, elems0 + sizeof(T)*elems.size()); |
| + |
| + return data_body; |
| +} |
| + |
| +// Custom specialization for strings. |
| +// Given a vector of strings, returns a vector of all the characters in strings. |
| +template<> |
| +std::vector<uint8> Flatten(const std::vector<std::string>& strings) { |
| + std::vector<uint8> totalchars; |
| + |
| + for(std::vector<std::string>::const_iterator it = strings.begin(); |
| + it != strings.end(); it++) { |
| + std::copy(it->begin(), it->end(), std::back_inserter(totalchars)); |
| + totalchars.push_back('\0'); // Add the null termination too. |
| + } |
| + |
| + return totalchars; |
| +} |
| + |
| +std::vector<uint8> Combined(const std::vector<uint8>& a, |
|
vandebo (ex-Chrome)
2013/03/28 00:56:04
CombineVectors ?
tommycli
2013/03/29 00:16:01
Done.
|
| + const std::vector<uint8>& b) { |
| + std::vector<uint8> total; |
| + |
| + std::copy(a.begin(), a.end(), std::back_inserter(total)); |
| + std::copy(b.begin(), b.end(), std::back_inserter(total)); |
| + |
| + return total; |
| +} |
| + |
| +// Writes memory into a temporary file for use in test |
| +bool ReaderReadFromMemory(PmpColumnReader* const reader, uint8* const data, |
| + int length, uint32* rows_read) { |
| + base::FilePath temppath; |
| + |
| + // Cast for usage in WriteFile function |
| + const char* data_char = reinterpret_cast<const char*>(data); |
| + |
| + if(!file_util::CreateTemporaryFile(&temppath) || |
| + file_util::WriteFile(temppath, data_char, length) != length) { |
| + return false; |
| + } |
| + |
| + return reader->ReadFromFile(temppath, rows_read); |
| +} |
| + |
| +// Overridden version of Read method to make test code templatable. |
| +bool DoRead(const PmpColumnReader* reader, uint32 row, std::string* target) { |
| + return reader->ReadString(row, target); |
| +} |
| + |
| +bool DoRead(const PmpColumnReader* reader, uint32 row, uint32* target) { |
| + return reader->ReadUInt32(row, target); |
| +} |
| + |
| +bool DoRead(const PmpColumnReader* reader, uint32 row, double* target) { |
| + return reader->ReadDouble64(row, target); |
| +} |
| + |
| +bool DoRead(const PmpColumnReader* reader, uint32 row, uint8* target) { |
| + return reader->ReadUInt8(row, target); |
| +} |
| + |
| +bool DoRead(const PmpColumnReader* reader, uint32 row, uint64* target) { |
| + return reader->ReadUInt64(row, target); |
| +} |
| + |
| +// TestValid |
| +template<class T> |
| +void TestValid(PmpColumnReader* reader, const uint16 field_type, |
| + const std::vector<T>& elems) { |
| + uint32 rows_read = 0xFF; |
| + |
| + std::vector<uint8> data = Combined(MakeHeader(field_type, elems.size()), |
| + Flatten(elems)); |
| + EXPECT_TRUE(ReaderReadFromMemory(reader, &data[0], data.size(), &rows_read)); |
| + EXPECT_EQ(rows_read, elems.size()); |
| + |
| + for(uint32 i = 0; i < elems.size(); i++) { |
| + T target; |
| + EXPECT_TRUE(DoRead(reader, i, &target)); |
| + EXPECT_EQ(target, elems[i]); |
| + } |
| +} |
| + |
| +template<class T> |
| +void TestMalformed(PmpColumnReader* reader, const uint16 field_type, |
| + const std::vector<T>& elems) { |
| + |
| + std::vector<uint8> data_too_few_declared_rows = |
| + Combined(MakeHeader(field_type, elems.size()-1), Flatten(elems)); |
| + EXPECT_FALSE(ReaderReadFromMemory(reader, &data_too_few_declared_rows[0], |
| + data_too_few_declared_rows.size(), NULL)); |
| + |
| + std::vector<uint8> data_too_many_declared_rows = |
| + Combined(MakeHeader(field_type, elems.size()+1), Flatten(elems)); |
| + EXPECT_FALSE(ReaderReadFromMemory(reader, &data_too_many_declared_rows[0], |
| + data_too_many_declared_rows.size(), NULL)); |
| + |
| + std::vector<uint8> data_truncated = |
| + Combined(MakeHeader(field_type, elems.size()), Flatten(elems)); |
| + data_truncated.resize(data_truncated.size()-10); |
| + |
| + EXPECT_FALSE(ReaderReadFromMemory(reader, &data_truncated[0], |
| + data_truncated.size(), NULL)); |
| + |
| + std::vector<uint8> data_padded = |
| + Combined(MakeHeader(field_type, elems.size()), Flatten(elems)); |
| + data_padded.resize(data_padded.size()+10); |
| + EXPECT_FALSE(ReaderReadFromMemory(reader, &data_padded[0], |
| + data_padded.size(), NULL)); |
| +} |
| + |
| +template<class T> |
| +void TestPrimitives(const uint16 field_type) { |
| + PmpColumnReader reader("testcolumn"); |
| + |
| + // Make an ascending vector of the primitive. |
| + uint32 n = 100; |
| + std::vector<T> data(n, 0); |
| + for(uint32 i = 0; i < n; i++) { |
| + data[i] = i*3; |
| + } |
| + |
| + TestValid<T>(&reader, field_type, data); |
| + TestMalformed<T>(&reader, field_type, data); |
| +} |
| + |
| + |
| +TEST(PmpColumnReaderTest, HeaderParsingAndValidation) { |
| + PmpColumnReader reader("testcolumn"); |
| + |
| + // Good header. |
| + uint32 rows_read = 0xFF; |
| + std::vector<uint8> good_header = MakeHeader(0x00, 0); |
| + EXPECT_TRUE(ReaderReadFromMemory(&reader, &good_header[0], |
| + good_header.size(), &rows_read)); |
| + EXPECT_EQ(rows_read, 0U) << "Read non-zero rows from header-only data."; |
| + |
| + // Botch up elements of the header. |
| + std::vector<uint8> bad_magic_byte = MakeHeader(0x00, 0); |
| + bad_magic_byte[0] = 0xff; |
| + EXPECT_FALSE(ReaderReadFromMemory(&reader, &bad_magic_byte[0], |
| + bad_magic_byte.size(), NULL)); |
| + |
| + // Corrupt means the type fields don't agree. |
| + std::vector<uint8> corrupt_type = MakeHeader(0x00, 0); |
| + corrupt_type[fileapi::kPmpFieldType1Offset] = 0xff; |
| + EXPECT_FALSE(ReaderReadFromMemory(&reader, &corrupt_type[0], |
| + corrupt_type.size(), NULL)); |
| + |
| + std::vector<uint8> invalid_type = MakeHeader(0x00, 0); |
| + invalid_type[fileapi::kPmpFieldType1Offset] = 0xff; |
| + invalid_type[fileapi::kPmpFieldType2Offset] = 0xff; |
| + EXPECT_FALSE(ReaderReadFromMemory(&reader, &invalid_type[0], |
| + invalid_type.size(), NULL)); |
| + |
| + std::vector<uint8> incomplete_header = MakeHeader(0x00, 0); |
| + incomplete_header.resize(10); |
| + EXPECT_FALSE(ReaderReadFromMemory(&reader, &incomplete_header[0], |
| + incomplete_header.size(), NULL)); |
| +} |
| + |
| +TEST(PmpColumnReaderTest, StringParsing) { |
| + PmpColumnReader reader("testcolumn"); |
| + |
| + std::vector<std::string> empty_strings(100, ""); |
| + |
| + // Test empty strings read okay. |
| + TestValid(&reader, fileapi::kPmpFieldTypeString, empty_strings); |
| + |
| + std::vector<std::string> mixed_strings; |
| + mixed_strings.push_back(""); |
| + mixed_strings.push_back("Hello"); |
| + mixed_strings.push_back("World"); |
| + mixed_strings.push_back(""); |
| + mixed_strings.push_back("123123"); |
| + mixed_strings.push_back("Q"); |
| + mixed_strings.push_back(""); |
| + |
| + // Test that a mixed set of strings read correctly. |
| + TestValid(&reader, fileapi::kPmpFieldTypeString, mixed_strings); |
| + |
| + // Test with the data messed up in a variety of ways. |
| + TestMalformed(&reader, fileapi::kPmpFieldTypeString, mixed_strings); |
| +} |
| + |
| +TEST(PmpColumnReaderTest, PrimitiveParsing) { |
| + TestPrimitives<uint32>(fileapi::kPmpFieldTypeUInt32); |
| + TestPrimitives<double>(fileapi::kPmpFieldTypeDouble64); |
| + TestPrimitives<uint8>(fileapi::kPmpFieldTypeUInt8); |
| + TestPrimitives<uint64>(fileapi::kPmpFieldTypeUInt64); |
| +} |
| + |
| +} // namespace |