// (C) Copyright 2017, Google Inc. // 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. #include "genericvector.h" #include "serialis.h" #include "include_gunit.h" using tesseract::TFile; namespace { // Tests TFile and GenericVector serialization by serializing and // writing/reading. class TfileTest : public ::testing::Test { protected: TfileTest() {} // Some data to serialize. class MathData { public: MathData() : num_squares_(0), num_triangles_(0) {} void Setup() { // Setup some data. for (int s = 0; s < 42; ++s) squares_.push_back(s * s); num_squares_ = squares_.size(); for (int t = 0; t < 52; ++t) triangles_.push_back(t * (t + 1) / 2); num_triangles_ = triangles_.size(); } void ExpectEq(const MathData& other) { // Check the data. EXPECT_EQ(num_squares_, other.num_squares_); for (int s = 0; s < squares_.size(); ++s) EXPECT_EQ(squares_[s], other.squares_[s]); EXPECT_EQ(num_triangles_, other.num_triangles_); for (int s = 0; s < triangles_.size(); ++s) EXPECT_EQ(triangles_[s], other.triangles_[s]); } bool Serialize(TFile* fp) { if (fp->FWrite(&num_squares_, sizeof(num_squares_), 1) != 1) return false; if (!squares_.Serialize(fp)) return false; if (fp->FWrite(&num_triangles_, sizeof(num_triangles_), 1) != 1) return false; if (!triangles_.Serialize(fp)) return false; return true; } bool DeSerialize(TFile* fp) { if (fp->FReadEndian(&num_squares_, sizeof(num_squares_), 1) != 1) return false; if (!squares_.DeSerialize(fp)) return false; if (fp->FReadEndian(&num_triangles_, sizeof(num_triangles_), 1) != 1) return false; if (!triangles_.DeSerialize(fp)) return false; return true; } bool SerializeBigEndian(TFile* fp) { ReverseN(&num_squares_, sizeof(num_squares_)); if (fp->FWrite(&num_squares_, sizeof(num_squares_), 1) != 1) return false; // Write an additional reversed size before the vector, which will get // used as its size on reading. if (fp->FWrite(&num_squares_, sizeof(num_squares_), 1) != 1) return false; for (int i = 0; i < squares_.size(); ++i) ReverseN(&squares_[i], sizeof(squares_[i])); if (!squares_.Serialize(fp)) return false; ReverseN(&num_triangles_, sizeof(num_triangles_)); if (fp->FWrite(&num_triangles_, sizeof(num_triangles_), 1) != 1) return false; if (fp->FWrite(&num_triangles_, sizeof(num_triangles_), 1) != 1) return false; for (int i = 0; i < triangles_.size(); ++i) ReverseN(&triangles_[i], sizeof(triangles_[i])); return triangles_.Serialize(fp); } bool DeSerializeBigEndian(TFile* fp) { if (fp->FReadEndian(&num_squares_, sizeof(num_squares_), 1) != 1) return false; if (!squares_.DeSerialize(fp)) return false; // The first element is the size that was written, so we will delete it // and read the last element separately. int last_element; if (fp->FReadEndian(&last_element, sizeof(last_element), 1) != 1) return false; squares_.remove(0); squares_.push_back(last_element); if (fp->FReadEndian(&num_triangles_, sizeof(num_triangles_), 1) != 1) return false; if (!triangles_.DeSerialize(fp)) return false; if (fp->FReadEndian(&last_element, sizeof(last_element), 1) != 1) return false; triangles_.remove(0); triangles_.push_back(last_element); return true; } private: GenericVector squares_; int num_squares_; GenericVector triangles_; int num_triangles_; }; }; TEST_F(TfileTest, Serialize) { // This test verifies that Tfile can serialize a class. MathData m1; m1.Setup(); GenericVector data; TFile fpw; fpw.OpenWrite(&data); EXPECT_TRUE(m1.Serialize(&fpw)); TFile fpr; EXPECT_TRUE(fpr.Open(&data[0], data.size())); MathData m2; EXPECT_TRUE(m2.DeSerialize(&fpr)); m1.ExpectEq(m2); MathData m3; EXPECT_FALSE(m3.DeSerialize(&fpr)); fpr.Rewind(); EXPECT_TRUE(m3.DeSerialize(&fpr)); m1.ExpectEq(m3); } TEST_F(TfileTest, FGets) { // This test verifies that Tfile can interleave FGets with binary data. MathData m1; std::string line_str = "This is a textline with a newline\n"; m1.Setup(); GenericVector data; TFile fpw; fpw.OpenWrite(&data); EXPECT_TRUE(m1.Serialize(&fpw)); EXPECT_EQ(1, fpw.FWrite(line_str.data(), line_str.size(), 1)); EXPECT_TRUE(m1.Serialize(&fpw)); // Now get back the 2 copies of m1 with the line in between. TFile fpr; EXPECT_TRUE(fpr.Open(&data[0], data.size())); MathData m2; EXPECT_TRUE(m2.DeSerialize(&fpr)); m1.ExpectEq(m2); const int kBufsize = 1024; char buffer[kBufsize + 1]; EXPECT_EQ(buffer, fpr.FGets(buffer, kBufsize)); EXPECT_STREQ(line_str.c_str(), buffer); MathData m3; EXPECT_TRUE(m3.DeSerialize(&fpr)); m1.ExpectEq(m3); } TEST_F(TfileTest, BigEndian) { // This test verifies that Tfile can auto-reverse big-endian data. MathData m1; m1.Setup(); GenericVector data; TFile fpw; fpw.OpenWrite(&data); EXPECT_TRUE(m1.SerializeBigEndian(&fpw)); TFile fpr; EXPECT_TRUE(fpr.Open(&data[0], data.size())); fpr.set_swap(true); MathData m2; EXPECT_TRUE(m2.DeSerializeBigEndian(&fpr)); // That serialize was destructive, so test against a fresh MathData. MathData m3; m3.Setup(); m3.ExpectEq(m2); } } // namespace