Fix bugs and add unit tests to FileInputStream class.
Add the class to Linux/Mac builds.



git-svn-id: http://sfntly.googlecode.com/svn/trunk/cpp/src@7 672e30a5-4c29-85ac-ac6d-611c735e0a51
diff --git a/sfntly/port/file_input_stream.cc b/sfntly/port/file_input_stream.cc
index dcb37f7..d4432bb 100644
--- a/sfntly/port/file_input_stream.cc
+++ b/sfntly/port/file_input_stream.cc
@@ -59,6 +59,7 @@
     fclose(file_);
     length_ = 0;
     position_ = 0;
+    file_ = NULL;
   }
 }
 
@@ -98,7 +99,6 @@
 
 int32_t FileInputStream::read(ByteVector* b, int32_t offset, int32_t length) {
   assert(b);
-  assert(b->size() >= (size_t)(offset + length));
   if (!file_) {
 #if defined (SFNTLY_NO_EXCEPTION)
     return 0;
@@ -114,6 +114,9 @@
 #endif
   }
   size_t read_count = std::min<size_t>(length_ - position_, length);
+  if (b->size() < (size_t)(offset + read_count)) {
+    b->resize((size_t)(offset + read_count));
+  }
   int32_t actual_read = fread(&((*b)[offset]), 1, read_count, file_);
   position_ += actual_read;
   return actual_read;
@@ -131,12 +134,16 @@
     throw IOException("no opened file");
 #endif
   }
-  if (n < 0) {
-    return 0;
+  int64_t skip_count = 0;
+  if (n < 0) {  // move backwards
+    skip_count = std::max<int64_t>(0 - (int64_t)position_, n);
+    position_ -= (size_t)(0 - skip_count);
+    fseek(file_, position_, SEEK_SET);
+  } else {
+    skip_count = std::min<size_t>(length_ - position_, (size_t)n);
+    position_ += (size_t)skip_count;
+    fseek(file_, (size_t)skip_count, SEEK_CUR);
   }
-  size_t skip_count = std::min<size_t>(length_ - position_, (size_t)n);
-  fseek(file_, skip_count, SEEK_CUR);
-  position_ += skip_count;
   return skip_count;
 }
 
@@ -156,8 +163,10 @@
   }
   size_t unread_count = std::min<size_t>(position_, length);
   fseek(file_, position_ - unread_count, SEEK_SET);
+  position_ -= unread_count;
   read(b, offset, length);
   fseek(file_, position_ - unread_count, SEEK_SET);
+  position_ -= unread_count;
 }
 
 }  // namespace sfntly
diff --git a/test/file_io_test.cc b/test/file_io_test.cc
new file mode 100644
index 0000000..f3b93ea
--- /dev/null
+++ b/test/file_io_test.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * 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 <stdio.h>
+
+#include "gtest/gtest.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/data/font_input_stream.h"
+
+namespace sfntly {
+
+bool testFileInputStream() {
+  const char* test_file = "arial.ttf";
+    FILE* file_handle = NULL;
+#if defined (WIN32)
+  fopen_s(&file_handle, test_file, "rb");
+#else
+  file_handle = fopen(test_file, "rb");
+#endif
+  if (file_handle == NULL) {
+    return false;
+  }
+  fseek(file_handle, 0, SEEK_END);
+  size_t length = ftell(file_handle);
+  fseek(file_handle, 0, SEEK_SET);
+  ByteVector b1;
+  b1.resize(length);
+  fread(&(b1[0]), 1, length, file_handle);
+  fclose(file_handle);
+
+  // Full file reading test
+  FileInputStream is;
+  is.open(test_file);
+  EXPECT_EQ(length, is.available());
+  ByteVector b2;
+  is.read(&b2, 0, length);
+  is.close();
+  EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), length), 0);
+  b2.clear();
+
+  // Partial reading test
+  is.open(test_file);
+  is.skip(89);
+  is.read(&b2, 0, 100);
+  EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0);
+  b2.clear();
+
+  // Skip test
+  is.skip(-89);
+  is.read(&b2, 0, 100);
+  EXPECT_EQ(memcmp(&(b1[100]), &(b2[0]), 100), 0);
+  b2.clear();
+  is.skip(100);
+  is.read(&b2, 0, 100);
+  EXPECT_EQ(memcmp(&(b1[300]), &(b2[0]), 100), 0);
+  is.skip(-400);
+  b2.clear();
+
+  // Offset test
+  is.read(&b2, 0, 100);
+  is.read(&b2, 100, 100);
+  EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), 200), 0);
+
+  // Unread test
+  ByteVector b3;
+  b3.resize(200);
+  is.unread(&b3);
+  EXPECT_EQ(memcmp(&(b3[0]), &(b2[0]), 200), 0);
+
+  return true;
+}
+
+bool testFontInputStream() {
+  const char* test_file = "arial.ttf";
+    FILE* file_handle = NULL;
+#if defined (WIN32)
+  fopen_s(&file_handle, test_file, "rb");
+#else
+  file_handle = fopen(test_file, "rb");
+#endif
+  if (file_handle == NULL) {
+    return false;
+  }
+  fseek(file_handle, 0, SEEK_END);
+  size_t length = ftell(file_handle);
+  fseek(file_handle, 0, SEEK_SET);
+  ByteVector b1;
+  b1.resize(length);
+  fread(&(b1[0]), 1, length, file_handle);
+  fclose(file_handle);
+
+  FileInputStream is;
+  is.open(test_file);
+  FontInputStream font_is1(&is);
+  EXPECT_EQ(font_is1.available(), length);
+
+  ByteVector b2;
+  font_is1.read(&b2, 0, length);
+  font_is1.close();
+  EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), length), 0);
+  b2.clear();
+
+  is.open(test_file);
+  is.skip(89);
+  FontInputStream font_is2(&is, 200);
+  font_is2.read(&b2, 0, 100);
+  EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0);
+  font_is2.read(&b2, 100, 100);
+  EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 200), 0);
+  b2.clear();
+  font_is2.skip(-200);
+  font_is2.read(&b2, 0, 100);
+  EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0);
+
+  return true;
+}
+
+}  // namespace sfntly
diff --git a/test/file_io_test.h b/test/file_io_test.h
new file mode 100644
index 0000000..f929ab4
--- /dev/null
+++ b/test/file_io_test.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * 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 TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_FILE_IO_TEST_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_FILE_IO_TEST_H_
+
+namespace sfntly {
+
+bool testFileInputStream();
+bool testFontInputStream();
+
+}  // namespace sfntly
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_FILE_IO_TEST_H_
diff --git a/test/main.cc b/test/main.cc
index 27862c5..ea17ae3 100644
--- a/test/main.cc
+++ b/test/main.cc
@@ -22,6 +22,7 @@
 #include "test/smart_pointer_test.h"
 #include "test/endian_test.h"
 #include "test/byte_array_test.h"
+#include "test/file_io_test.h"
 #include "test/font_data_test.h"
 #include "test/open_type_data_test.h"
 #include "test/otf_basic_editing_test.h"
@@ -50,6 +51,11 @@
 }
 #endif
 
+TEST(FileIO, All) {
+  EXPECT_TRUE(sfntly::testFileInputStream());
+  EXPECT_TRUE(sfntly::testFontInputStream());
+}
+
 TEST(OpenTypeData, All) {
   EXPECT_TRUE(sfntly::testOTFRead());
   EXPECT_TRUE(sfntly::testOTFCopy());
diff --git a/test/test_font_utils.cc b/test/test_font_utils.cc
new file mode 100644
index 0000000..d8a7220
--- /dev/null
+++ b/test/test_font_utils.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * 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 <stdio.h>
+
+#include "gtest/gtest.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/port/file_input_stream.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+void builderForFontFile(const char* font_path, FontFactory* factory,
+                        FontBuilderArray* builders) {
+  assert(factory);
+  FileInputStream is;
+  is.open(font_path);
+  factory->loadFontsForBuilding(&is, builders);
+  EXPECT_GT(builders->size(), static_cast<size_t>(0));
+}
+
+void serializeFont(const char* font_path, FontFactory* factory, Font* font) {
+  assert(font_path);
+  assert(factory);
+  assert(font);
+  MemoryOutputStream output_stream;
+  factory->serializeFont(font, &output_stream);
+  serializeToFile(&output_stream, font_path);
+}
+
+void loadFont(const char* font_path, FontFactory* factory, FontArray* fonts) {
+  FileInputStream is;
+  is.open(font_path);
+  factory->loadFonts(&is, fonts);
+  is.close();
+}
+
+void loadFile(const char* input_file_path, ByteVector* input_buffer) {
+  assert(input_file_path);
+  assert(input_buffer);
+
+  FILE* input_file = NULL;
+#if defined WIN32
+  fopen_s(&input_file, input_file_path, "rb");
+#else
+  input_file = fopen(input_file_path, "rb");
+#endif
+  EXPECT_NE(input_file, reinterpret_cast<FILE*>(NULL));
+  fseek(input_file, 0, SEEK_END);
+  size_t file_size = ftell(input_file);
+  fseek(input_file, 0, SEEK_SET);
+  input_buffer->resize(file_size);
+  fread(&((*input_buffer)[0]), 1, file_size, input_file);
+  fclose(input_file);
+}
+
+void serializeToFile(MemoryOutputStream* output_stream, const char* file_path) {
+  assert(file_path);
+  assert(output_stream);
+
+  FILE* output_file = NULL;
+#if defined WIN32
+  fopen_s(&output_file, file_path, "wb");
+#else
+  output_file = fopen(file_path, "wb");
+#endif
+  EXPECT_NE(output_file, reinterpret_cast<FILE*>(NULL));
+  fwrite(output_stream->get(), 1, output_stream->size(), output_file);
+  fflush(output_file);
+  fclose(output_file);
+}
+
+}  // namespace sfntly
diff --git a/test/test_font_utils.h b/test/test_font_utils.h
new file mode 100644
index 0000000..d3f516d
--- /dev/null
+++ b/test/test_font_utils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * 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 TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_TEST_FONT_UTILS_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_TEST_FONT_UTILS_H_
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/port/memory_output_stream.h"
+
+namespace sfntly {
+
+void builderForFontFile(const char* font_path, FontFactory* factory,
+                        FontBuilderArray* builders);
+void serializeFont(const char* font_path, FontFactory* factory, Font* font);
+void loadFont(const char* font_path, FontFactory* factory, FontArray* fonts);
+
+void loadFile(const char* input_file_path, ByteVector* input_buffer);
+void serializeToFile(MemoryOutputStream* output_stream, const char* file_path);
+
+}  // namespace sfntly
+
+#endif  // TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_TEST_FONT_UTILS_H_