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/files/file.h"
9#include "base/files/file_util.h"
10#include "base/files/scoped_temp_dir.h"
11#include "chrome/common/media_galleries/pmp_constants.h"
12#include "chrome/common/media_galleries/pmp_test_util.h"
13#include "chrome/utility/media_galleries/pmp_column_reader.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace picasa {
17
18namespace {
19
20bool InitColumnReaderFromBytes(
21    PmpColumnReader* const reader,
22    const std::vector<char>& data,
23    const PmpFieldType expected_type) {
24  base::ScopedTempDir temp_dir;
25  if (!temp_dir.CreateUniqueTempDir())
26    return false;
27
28  base::FilePath temp_path;
29  if (!base::CreateTemporaryFileInDir(temp_dir.path(), &temp_path))
30    return false;
31
32  // Explicit conversion from signed to unsigned.
33  size_t bytes_written = base::WriteFile(temp_path, &data[0], data.size());
34  if (bytes_written != data.size())
35    return false;
36
37  base::File file(temp_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
38  if (!file.IsValid())
39    return false;
40
41  return reader->ReadFile(&file, expected_type);
42}
43
44// Overridden version of Read method to make test code templatable.
45bool DoRead(const PmpColumnReader* reader, uint32 row, std::string* target) {
46  return reader->ReadString(row, target);
47}
48
49bool DoRead(const PmpColumnReader* reader, uint32 row, uint32* target) {
50  return reader->ReadUInt32(row, target);
51}
52
53bool DoRead(const PmpColumnReader* reader, uint32 row, double* target) {
54  return reader->ReadDouble64(row, target);
55}
56
57bool DoRead(const PmpColumnReader* reader, uint32 row, uint8* target) {
58  return reader->ReadUInt8(row, target);
59}
60
61bool DoRead(const PmpColumnReader* reader, uint32 row, uint64* target) {
62  return reader->ReadUInt64(row, target);
63}
64
65// TestValid
66template<class T>
67void TestValid(const PmpFieldType field_type,
68               const std::vector<T>& elems) {
69  PmpColumnReader reader;
70  std::vector<char> data =
71      PmpTestUtil::MakeHeaderAndBody(field_type, elems.size(), elems);
72  ASSERT_TRUE(InitColumnReaderFromBytes(&reader, data, field_type));
73  EXPECT_EQ(elems.size(), reader.rows_read());
74
75  for (uint32 i = 0; i < elems.size() && i < reader.rows_read(); i++) {
76    T target;
77    EXPECT_TRUE(DoRead(&reader, i, &target));
78    EXPECT_EQ(elems[i], target);
79  }
80}
81
82template<class T>
83void TestMalformed(const PmpFieldType field_type,
84                   const std::vector<T>& elems) {
85  PmpColumnReader reader_too_few_declared_rows;
86  std::vector<char> data_too_few_declared_rows =
87      PmpTestUtil::MakeHeaderAndBody(field_type, elems.size()-1, elems);
88  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_too_few_declared_rows,
89                                         data_too_few_declared_rows,
90                                         field_type));
91
92  PmpColumnReader reader_too_many_declared_rows;
93  std::vector<char> data_too_many_declared_rows =
94      PmpTestUtil::MakeHeaderAndBody(field_type, elems.size()+1, elems);
95  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_too_many_declared_rows,
96                                         data_too_many_declared_rows,
97                                         field_type));
98
99  PmpColumnReader reader_truncated;
100  std::vector<char> data_truncated =
101      PmpTestUtil::MakeHeaderAndBody(field_type, elems.size(), elems);
102  data_truncated.resize(data_truncated.size()-10);
103  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_truncated,
104                                         data_truncated,
105                                         field_type));
106
107  PmpColumnReader reader_padded;
108  std::vector<char> data_padded =
109      PmpTestUtil::MakeHeaderAndBody(field_type, elems.size(), elems);
110  data_padded.resize(data_padded.size()+10);
111  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_padded,
112                                         data_padded,
113                                         field_type));
114}
115
116template<class T>
117void TestPrimitive(const PmpFieldType field_type) {
118  // Make an ascending vector of the primitive.
119  uint32 n = 100;
120  std::vector<T> data(n, 0);
121  for (uint32 i = 0; i < n; i++) {
122    data[i] = i*3;
123  }
124
125  TestValid<T>(field_type, data);
126  TestMalformed<T>(field_type, data);
127}
128
129
130TEST(PmpColumnReaderTest, HeaderParsingAndValidation) {
131  PmpColumnReader reader_good_header;
132  std::vector<char> good_header =
133      PmpTestUtil::MakeHeader(PMP_TYPE_STRING, 0);
134  EXPECT_TRUE(InitColumnReaderFromBytes(&reader_good_header,
135                                        good_header,
136                                        PMP_TYPE_STRING));
137  EXPECT_EQ(0U, reader_good_header.rows_read()) <<
138      "Read non-zero rows from header-only data.";
139
140  PmpColumnReader reader_bad_magic_bytes;
141  std::vector<char> bad_magic_bytes =
142      PmpTestUtil::MakeHeader(PMP_TYPE_STRING, 0);
143  bad_magic_bytes[0] = static_cast<char>(-128);
144  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_bad_magic_bytes,
145                                         bad_magic_bytes,
146                                         PMP_TYPE_STRING));
147
148  PmpColumnReader reader_inconsistent_types;
149  std::vector<char> inconsistent_type =
150      PmpTestUtil::MakeHeader(PMP_TYPE_STRING, 0);
151  inconsistent_type[kPmpFieldType1Offset] = static_cast<char>(-128);
152  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_inconsistent_types,
153                                         inconsistent_type,
154                                         PMP_TYPE_STRING));
155
156  PmpColumnReader reader_invalid_type;
157  std::vector<char> invalid_type =
158      PmpTestUtil::MakeHeader(PMP_TYPE_STRING, 0);
159  invalid_type[kPmpFieldType1Offset] = static_cast<char>(-128);
160  invalid_type[kPmpFieldType2Offset] = static_cast<char>(-128);
161  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_invalid_type,
162                                         invalid_type,
163                                         PMP_TYPE_STRING));
164
165  PmpColumnReader reader_incomplete_header;
166  std::vector<char> incomplete_header =
167      PmpTestUtil::MakeHeader(PMP_TYPE_STRING, 0);
168  incomplete_header.resize(10);
169  EXPECT_FALSE(InitColumnReaderFromBytes(&reader_incomplete_header,
170                                         incomplete_header,
171                                         PMP_TYPE_STRING));
172}
173
174TEST(PmpColumnReaderTest, StringParsing) {
175  std::vector<std::string> empty_strings(100, "");
176
177  // Test empty strings read okay.
178  TestValid(PMP_TYPE_STRING, empty_strings);
179
180  std::vector<std::string> mixed_strings;
181  mixed_strings.push_back("");
182  mixed_strings.push_back("Hello");
183  mixed_strings.push_back("World");
184  mixed_strings.push_back("");
185  mixed_strings.push_back("123123");
186  mixed_strings.push_back("Q");
187  mixed_strings.push_back("");
188
189  // Test that a mixed set of strings read correctly.
190  TestValid(PMP_TYPE_STRING, mixed_strings);
191
192  // Test with the data messed up in a variety of ways.
193  TestMalformed(PMP_TYPE_STRING, mixed_strings);
194}
195
196TEST(PmpColumnReaderTest, PrimitiveParsing) {
197  TestPrimitive<uint32>(PMP_TYPE_UINT32);
198  TestPrimitive<double>(PMP_TYPE_DOUBLE64);
199  TestPrimitive<uint8>(PMP_TYPE_UINT8);
200  TestPrimitive<uint64>(PMP_TYPE_UINT64);
201}
202
203}  // namespace
204
205}  // namespace picasa
206