Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <algorithm> | |
| 6 #include <vector> | |
| 7 | |
| 8 #include "base/file_util.h" | |
| 9 #include "testing/gtest/include/gtest/gtest.h" | |
| 10 #include "webkit/fileapi/media/picasa/pmp_column_reader.h" | |
| 11 #include "webkit/fileapi/media/picasa/pmp_constants.h" | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 using fileapi::PmpColumnReader; | |
| 16 | |
| 17 // Return a vector so we don't have to worry about memory management. | |
| 18 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
| |
| 19 std::vector<uint8> header(fileapi::kPmpHeaderSize); | |
| 20 | |
| 21 // Copy in magic bytes. | |
| 22 memcpy(&header[fileapi::kPmpMagic1Offset], &fileapi::kPmpMagic1, | |
| 23 sizeof(fileapi::kPmpMagic1)); | |
| 24 memcpy(&header[fileapi::kPmpMagic2Offset], &fileapi::kPmpMagic2, | |
| 25 sizeof(fileapi::kPmpMagic2)); | |
| 26 memcpy(&header[fileapi::kPmpMagic3Offset], &fileapi::kPmpMagic3, | |
| 27 sizeof(fileapi::kPmpMagic3)); | |
| 28 memcpy(&header[fileapi::kPmpMagic4Offset], &fileapi::kPmpMagic4, | |
| 29 sizeof(fileapi::kPmpMagic4)); | |
| 30 | |
| 31 // Copy in field type. | |
| 32 memcpy(&header[fileapi::kPmpFieldType1Offset], &field_type, 2); | |
| 33 memcpy(&header[fileapi::kPmpFieldType2Offset], &field_type, 2); | |
| 34 | |
| 35 // Copy in row count. | |
| 36 memcpy(&header[fileapi::kPmpRowCountOffset], &row_count, 4); | |
| 37 | |
| 38 return header; | |
| 39 } | |
| 40 | |
| 41 // Flatten a vector of elements into an array of bytes. | |
| 42 template<class T> | |
| 43 std::vector<uint8> Flatten(const std::vector<T>& elems) { | |
| 44 const uint8* elems0 = reinterpret_cast<const uint8*>(&elems[0]); | |
| 45 std::vector<uint8> data_body(elems0, elems0 + sizeof(T)*elems.size()); | |
| 46 | |
| 47 return data_body; | |
| 48 } | |
| 49 | |
| 50 // Custom specialization for strings. | |
| 51 // Given a vector of strings, returns a vector of all the characters in strings. | |
| 52 template<> | |
| 53 std::vector<uint8> Flatten(const std::vector<std::string>& strings) { | |
| 54 std::vector<uint8> totalchars; | |
| 55 | |
| 56 for(std::vector<std::string>::const_iterator it = strings.begin(); | |
| 57 it != strings.end(); it++) { | |
| 58 std::copy(it->begin(), it->end(), std::back_inserter(totalchars)); | |
| 59 totalchars.push_back('\0'); // Add the null termination too. | |
| 60 } | |
| 61 | |
| 62 return totalchars; | |
| 63 } | |
| 64 | |
| 65 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.
| |
| 66 const std::vector<uint8>& b) { | |
| 67 std::vector<uint8> total; | |
| 68 | |
| 69 std::copy(a.begin(), a.end(), std::back_inserter(total)); | |
| 70 std::copy(b.begin(), b.end(), std::back_inserter(total)); | |
| 71 | |
| 72 return total; | |
| 73 } | |
| 74 | |
| 75 // Writes memory into a temporary file for use in test | |
| 76 bool ReaderReadFromMemory(PmpColumnReader* const reader, uint8* const data, | |
| 77 int length, uint32* rows_read) { | |
| 78 base::FilePath temppath; | |
| 79 | |
| 80 // Cast for usage in WriteFile function | |
| 81 const char* data_char = reinterpret_cast<const char*>(data); | |
| 82 | |
| 83 if(!file_util::CreateTemporaryFile(&temppath) || | |
| 84 file_util::WriteFile(temppath, data_char, length) != length) { | |
| 85 return false; | |
| 86 } | |
| 87 | |
| 88 return reader->ReadFromFile(temppath, rows_read); | |
| 89 } | |
| 90 | |
| 91 // Overridden version of Read method to make test code templatable. | |
| 92 bool DoRead(const PmpColumnReader* reader, uint32 row, std::string* target) { | |
| 93 return reader->ReadString(row, target); | |
| 94 } | |
| 95 | |
| 96 bool DoRead(const PmpColumnReader* reader, uint32 row, uint32* target) { | |
| 97 return reader->ReadUInt32(row, target); | |
| 98 } | |
| 99 | |
| 100 bool DoRead(const PmpColumnReader* reader, uint32 row, double* target) { | |
| 101 return reader->ReadDouble64(row, target); | |
| 102 } | |
| 103 | |
| 104 bool DoRead(const PmpColumnReader* reader, uint32 row, uint8* target) { | |
| 105 return reader->ReadUInt8(row, target); | |
| 106 } | |
| 107 | |
| 108 bool DoRead(const PmpColumnReader* reader, uint32 row, uint64* target) { | |
| 109 return reader->ReadUInt64(row, target); | |
| 110 } | |
| 111 | |
| 112 // TestValid | |
| 113 template<class T> | |
| 114 void TestValid(PmpColumnReader* reader, const uint16 field_type, | |
| 115 const std::vector<T>& elems) { | |
| 116 uint32 rows_read = 0xFF; | |
| 117 | |
| 118 std::vector<uint8> data = Combined(MakeHeader(field_type, elems.size()), | |
| 119 Flatten(elems)); | |
| 120 EXPECT_TRUE(ReaderReadFromMemory(reader, &data[0], data.size(), &rows_read)); | |
| 121 EXPECT_EQ(rows_read, elems.size()); | |
| 122 | |
| 123 for(uint32 i = 0; i < elems.size(); i++) { | |
| 124 T target; | |
| 125 EXPECT_TRUE(DoRead(reader, i, &target)); | |
| 126 EXPECT_EQ(target, elems[i]); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 template<class T> | |
| 131 void TestMalformed(PmpColumnReader* reader, const uint16 field_type, | |
| 132 const std::vector<T>& elems) { | |
| 133 | |
| 134 std::vector<uint8> data_too_few_declared_rows = | |
| 135 Combined(MakeHeader(field_type, elems.size()-1), Flatten(elems)); | |
| 136 EXPECT_FALSE(ReaderReadFromMemory(reader, &data_too_few_declared_rows[0], | |
| 137 data_too_few_declared_rows.size(), NULL)); | |
| 138 | |
| 139 std::vector<uint8> data_too_many_declared_rows = | |
| 140 Combined(MakeHeader(field_type, elems.size()+1), Flatten(elems)); | |
| 141 EXPECT_FALSE(ReaderReadFromMemory(reader, &data_too_many_declared_rows[0], | |
| 142 data_too_many_declared_rows.size(), NULL)); | |
| 143 | |
| 144 std::vector<uint8> data_truncated = | |
| 145 Combined(MakeHeader(field_type, elems.size()), Flatten(elems)); | |
| 146 data_truncated.resize(data_truncated.size()-10); | |
| 147 | |
| 148 EXPECT_FALSE(ReaderReadFromMemory(reader, &data_truncated[0], | |
| 149 data_truncated.size(), NULL)); | |
| 150 | |
| 151 std::vector<uint8> data_padded = | |
| 152 Combined(MakeHeader(field_type, elems.size()), Flatten(elems)); | |
| 153 data_padded.resize(data_padded.size()+10); | |
| 154 EXPECT_FALSE(ReaderReadFromMemory(reader, &data_padded[0], | |
| 155 data_padded.size(), NULL)); | |
| 156 } | |
| 157 | |
| 158 template<class T> | |
| 159 void TestPrimitives(const uint16 field_type) { | |
| 160 PmpColumnReader reader("testcolumn"); | |
| 161 | |
| 162 // Make an ascending vector of the primitive. | |
| 163 uint32 n = 100; | |
| 164 std::vector<T> data(n, 0); | |
| 165 for(uint32 i = 0; i < n; i++) { | |
| 166 data[i] = i*3; | |
| 167 } | |
| 168 | |
| 169 TestValid<T>(&reader, field_type, data); | |
| 170 TestMalformed<T>(&reader, field_type, data); | |
| 171 } | |
| 172 | |
| 173 | |
| 174 TEST(PmpColumnReaderTest, HeaderParsingAndValidation) { | |
| 175 PmpColumnReader reader("testcolumn"); | |
| 176 | |
| 177 // Good header. | |
| 178 uint32 rows_read = 0xFF; | |
| 179 std::vector<uint8> good_header = MakeHeader(0x00, 0); | |
| 180 EXPECT_TRUE(ReaderReadFromMemory(&reader, &good_header[0], | |
| 181 good_header.size(), &rows_read)); | |
| 182 EXPECT_EQ(rows_read, 0U) << "Read non-zero rows from header-only data."; | |
| 183 | |
| 184 // Botch up elements of the header. | |
| 185 std::vector<uint8> bad_magic_byte = MakeHeader(0x00, 0); | |
| 186 bad_magic_byte[0] = 0xff; | |
| 187 EXPECT_FALSE(ReaderReadFromMemory(&reader, &bad_magic_byte[0], | |
| 188 bad_magic_byte.size(), NULL)); | |
| 189 | |
| 190 // Corrupt means the type fields don't agree. | |
| 191 std::vector<uint8> corrupt_type = MakeHeader(0x00, 0); | |
| 192 corrupt_type[fileapi::kPmpFieldType1Offset] = 0xff; | |
| 193 EXPECT_FALSE(ReaderReadFromMemory(&reader, &corrupt_type[0], | |
| 194 corrupt_type.size(), NULL)); | |
| 195 | |
| 196 std::vector<uint8> invalid_type = MakeHeader(0x00, 0); | |
| 197 invalid_type[fileapi::kPmpFieldType1Offset] = 0xff; | |
| 198 invalid_type[fileapi::kPmpFieldType2Offset] = 0xff; | |
| 199 EXPECT_FALSE(ReaderReadFromMemory(&reader, &invalid_type[0], | |
| 200 invalid_type.size(), NULL)); | |
| 201 | |
| 202 std::vector<uint8> incomplete_header = MakeHeader(0x00, 0); | |
| 203 incomplete_header.resize(10); | |
| 204 EXPECT_FALSE(ReaderReadFromMemory(&reader, &incomplete_header[0], | |
| 205 incomplete_header.size(), NULL)); | |
| 206 } | |
| 207 | |
| 208 TEST(PmpColumnReaderTest, StringParsing) { | |
| 209 PmpColumnReader reader("testcolumn"); | |
| 210 | |
| 211 std::vector<std::string> empty_strings(100, ""); | |
| 212 | |
| 213 // Test empty strings read okay. | |
| 214 TestValid(&reader, fileapi::kPmpFieldTypeString, empty_strings); | |
| 215 | |
| 216 std::vector<std::string> mixed_strings; | |
| 217 mixed_strings.push_back(""); | |
| 218 mixed_strings.push_back("Hello"); | |
| 219 mixed_strings.push_back("World"); | |
| 220 mixed_strings.push_back(""); | |
| 221 mixed_strings.push_back("123123"); | |
| 222 mixed_strings.push_back("Q"); | |
| 223 mixed_strings.push_back(""); | |
| 224 | |
| 225 // Test that a mixed set of strings read correctly. | |
| 226 TestValid(&reader, fileapi::kPmpFieldTypeString, mixed_strings); | |
| 227 | |
| 228 // Test with the data messed up in a variety of ways. | |
| 229 TestMalformed(&reader, fileapi::kPmpFieldTypeString, mixed_strings); | |
| 230 } | |
| 231 | |
| 232 TEST(PmpColumnReaderTest, PrimitiveParsing) { | |
| 233 TestPrimitives<uint32>(fileapi::kPmpFieldTypeUInt32); | |
| 234 TestPrimitives<double>(fileapi::kPmpFieldTypeDouble64); | |
| 235 TestPrimitives<uint8>(fileapi::kPmpFieldTypeUInt8); | |
| 236 TestPrimitives<uint64>(fileapi::kPmpFieldTypeUInt64); | |
| 237 } | |
| 238 | |
| 239 } // namespace | |
| OLD | NEW |