summaryrefslogtreecommitdiffstats
path: root/gfx/ots/tests
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@wolfbeast.com>2019-11-14 10:07:01 +0100
committerwolfbeast <mcwerewolf@wolfbeast.com>2019-11-14 10:07:01 +0100
commit0f8691a48869932cd3de5195f5211c25e4691b21 (patch)
tree829675d64c457be0b447dfbcf9534cc22f83d392 /gfx/ots/tests
parent36975f3865948f3faa959fe386e58b22783bd379 (diff)
downloadUXP-0f8691a48869932cd3de5195f5211c25e4691b21.tar
UXP-0f8691a48869932cd3de5195f5211c25e4691b21.tar.gz
UXP-0f8691a48869932cd3de5195f5211c25e4691b21.tar.lz
UXP-0f8691a48869932cd3de5195f5211c25e4691b21.tar.xz
UXP-0f8691a48869932cd3de5195f5211c25e4691b21.zip
Issue #1288 - Part 4: Update the OpenType Sanitizer component to 8.0.0
Diffstat (limited to 'gfx/ots/tests')
-rw-r--r--gfx/ots/tests/cff_charstring_test.cc1588
-rw-r--r--gfx/ots/tests/layout_common_table_test.cc770
2 files changed, 2358 insertions, 0 deletions
diff --git a/gfx/ots/tests/cff_charstring_test.cc b/gfx/ots/tests/cff_charstring_test.cc
new file mode 100644
index 000000000..18e077e8f
--- /dev/null
+++ b/gfx/ots/tests/cff_charstring_test.cc
@@ -0,0 +1,1588 @@
+// Copyright (c) 2010-2017 The OTS 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 "cff_charstring.h"
+
+#include <gtest/gtest.h>
+
+#include <climits>
+#include <vector>
+
+#include "cff.h"
+
+// Returns a biased number for callsubr and callgsubr operators.
+#define GET_SUBR_NUMBER(n) ((n) - 107)
+#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
+
+namespace {
+
+// A constant which is used in AddSubr function below.
+const int kOpPrefix = INT_MAX;
+
+// Encodes an operator |op| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds.
+bool EncodeOperator(int op, std::vector<uint8_t> *out_bytes) {
+ if (op < 0) {
+ return false;
+ }
+ if (op <= 11) {
+ out_bytes->push_back(op);
+ return true;
+ }
+ if (op == 12) {
+ return false;
+ }
+ if (op <= 27) {
+ out_bytes->push_back(op);
+ return true;
+ }
+ if (op == 28) {
+ return false;
+ }
+ if (op <= 31) {
+ out_bytes->push_back(op);
+ return true;
+ }
+
+ const uint8_t upper = (op & 0xff00u) >> 8;
+ const uint8_t lower = op & 0xffu;
+ if (upper != 12) {
+ return false;
+ }
+ out_bytes->push_back(upper);
+ out_bytes->push_back(lower);
+ return true;
+}
+
+// Encodes a number |num| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds. The function does not support 16.16
+// Fixed number.
+bool EncodeNumber(int num, std::vector<uint8_t> *out_bytes) {
+ if (num >= -107 && num <= 107) {
+ out_bytes->push_back(num + 139);
+ return true;
+ }
+ if (num >= 108 && num <= 1131) {
+ const uint8_t v = ((num - 108) / 256) + 247;
+ const uint8_t w = (num - 108) % 256;
+ out_bytes->push_back(v);
+ out_bytes->push_back(w);
+ return true;
+ }
+ if (num <= -108 && num >= -1131) {
+ const uint8_t v = (-(num + 108) / 256) + 251;
+ const uint8_t w = -(num + 108) % 256;
+ out_bytes->push_back(v);
+ out_bytes->push_back(w);
+ return true;
+ }
+ if (num <= 32768 && num >= -32767) {
+ const uint8_t v = (num % 0xff00u) >> 8;
+ const uint8_t w = num % 0xffu;
+ out_bytes->push_back(28);
+ out_bytes->push_back(v);
+ out_bytes->push_back(w);
+ return true;
+ }
+ return false;
+}
+
+// Adds a subroutine |subr| to |out_buffer| and |out_subr|. The contents of the
+// subroutine is copied to |out_buffer|, and then the position of the subroutine
+// in |out_buffer| is written to |out_subr|. Returns true on success.
+bool AddSubr(const int *subr, size_t subr_len,
+ std::vector<uint8_t>* out_buffer, ots::CFFIndex *out_subr) {
+ size_t pre_offset = out_buffer->size();
+ for (size_t i = 0; i < subr_len; ++i) {
+ if (subr[i] != kOpPrefix) {
+ if (!EncodeNumber(subr[i], out_buffer)) {
+ return false;
+ }
+ } else {
+ if (i + 1 == subr_len) {
+ return false;
+ }
+ ++i;
+ if (!EncodeOperator(subr[i], out_buffer)) {
+ return false;
+ }
+ }
+ }
+
+ ++(out_subr->count);
+ out_subr->off_size = 1;
+ if (out_subr->offsets.empty()) {
+ out_subr->offsets.push_back(pre_offset);
+ }
+ out_subr->offsets.push_back(out_buffer->size());
+ return true;
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool Validate(const int *char_string, size_t char_string_len,
+ const int *global_subrs, size_t global_subrs_len,
+ const int *local_subrs, size_t local_subrs_len) {
+ std::vector<uint8_t> buffer;
+ ots::CFFIndex* char_strings_index = new ots::CFFIndex;
+ ots::CFFIndex global_subrs_index;
+ ots::CFFIndex* local_subrs_index = new ots::CFFIndex;
+
+ if (char_string) {
+ if (!AddSubr(char_string, char_string_len,
+ &buffer, char_strings_index)) {
+ return false;
+ }
+ }
+ if (global_subrs) {
+ if (!AddSubr(global_subrs, global_subrs_len,
+ &buffer, &global_subrs_index)) {
+ return false;
+ }
+ }
+ if (local_subrs) {
+ if (!AddSubr(local_subrs, local_subrs_len,
+ &buffer, local_subrs_index)) {
+ return false;
+ }
+ }
+
+ ots::Buffer ots_buffer(&buffer[0], buffer.size());
+
+ ots::FontFile* file = new ots::FontFile();
+ file->context = new ots::OTSContext();
+ ots::Font* font = new ots::Font(file);
+ ots::OpenTypeCFF* cff = new ots::OpenTypeCFF(font, OTS_TAG_CFF);
+ cff->charstrings_index = char_strings_index;
+ cff->local_subrs = local_subrs_index;
+ bool ret = ots::ValidateCFFCharStrings(*cff,
+ global_subrs_index,
+ &ots_buffer);
+ delete file->context;
+ delete file;
+ delete font;
+ delete cff;
+
+ return ret;
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool ValidateCharStrings(const int *char_string, size_t char_string_len) {
+ return Validate(char_string, char_string_len, NULL, 0, NULL, 0);
+}
+
+} // namespace
+
+TEST(ValidateTest, TestRMoveTo) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kRMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, // width
+ 1, 2, kOpPrefix, ots::kRMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kRMoveTo,
+ 1, 2, 3, kOpPrefix, ots::kRMoveTo, // invalid number of args
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHMoveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kHMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, // width
+ 1, kOpPrefix, ots::kHMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kHMoveTo,
+ 1, 2, kOpPrefix, ots::kHMoveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestVMoveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, // width
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, kOpPrefix, ots::kVMoveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestRLineTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, kOpPrefix, ots::kRLineTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, kOpPrefix, ots::kRLineTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, kOpPrefix, ots::kRLineTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kRLineTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHLineTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, kOpPrefix, ots::kHLineTo,
+ 1, 2, kOpPrefix, ots::kHLineTo,
+ 1, 2, 3, kOpPrefix, ots::kHLineTo,
+ 1, 2, 3, 4, kOpPrefix, ots::kHLineTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kHLineTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kHLineTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kHLineTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestVLineTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, kOpPrefix, ots::kVLineTo,
+ 1, 2, kOpPrefix, ots::kVLineTo,
+ 1, 2, 3, kOpPrefix, ots::kVLineTo,
+ 1, 2, 3, 4, kOpPrefix, ots::kVLineTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kVLineTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kVLineTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVLineTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestRRCurveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kRRCurveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kRRCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHHCurveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, kOpPrefix, ots::kHHCurveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHHCurveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, kOpPrefix, ots::kHHCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, kOpPrefix, ots::kHHCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHVCurveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ // The first form.
+ 1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kHVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kHVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ kOpPrefix, ots::kHVCurveTo,
+ // The second form.
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ kOpPrefix, ots::kHVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, kOpPrefix, ots::kHVCurveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, kOpPrefix, ots::kHVCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, kOpPrefix, ots::kHVCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestRCurveLine) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ kOpPrefix, ots::kRCurveLine,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRCurveLine, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ // can't be the first op.
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestRLineCurve) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kRLineCurve,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRLineCurve, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ // can't be the first op.
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestVHCurveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ // The first form.
+ 1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kVHCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kVHCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ kOpPrefix, ots::kVHCurveTo,
+ // The second form.
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVHCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVHCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ kOpPrefix, ots::kVHCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, kOpPrefix, ots::kVHCurveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, kOpPrefix, ots::kVHCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, kOpPrefix, ots::kVHCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestVVCurveTo) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kVVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVVCurveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVVCurveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kVVCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, kOpPrefix, ots::kVVCurveTo, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo, // can't be the first op.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestFlex) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kFlex, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kFlex, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHFlex) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kHFlex, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, kOpPrefix, ots::kHFlex, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHFlex1) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kHFlex1, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHFlex1, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestFlex1) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kFlex1, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kFlex1, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestEndChar) {
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ const int local_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+ NULL, 0,
+ local_subrs, ARRAYSIZE(local_subrs)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+ };
+ const int global_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ NULL, 0));
+ }
+}
+
+TEST(ValidateTest, TestHStem) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 0, // width
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 0, 1, 2, kOpPrefix, ots::kHStem, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kHStem, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestVStem) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kVStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kVStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 0, // width
+ 1, 2, kOpPrefix, ots::kVStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 0, 1, 2, kOpPrefix, ots::kVStem, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kVStem, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHStemHm) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStemHm,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kHStemHm,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 0, // width
+ 1, 2, kOpPrefix, ots::kHStemHm,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 0, 1, 2, kOpPrefix, ots::kHStemHm, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kHStemHm, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestVStemHm) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kVStemHm,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kVStemHm,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 0, // width
+ 1, 2, kOpPrefix, ots::kVStemHm,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 0, 1, 2, kOpPrefix, ots::kVStemHm, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kVMoveTo,
+ 1, 2, 3, 4, 5, kOpPrefix, ots::kVStemHm, // invalid
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestHintMask) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kHintMask, 0x00,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ 3, 4, 5, 6, kOpPrefix, ots::kHintMask, 0x00, // vstem
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kHintMask, 0x00, // no stems to mask
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ 3, 4, 5, kOpPrefix, ots::kHintMask, 0x00, // invalid vstem
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestCntrMask) {
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kCntrMask, 0x00,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ 3, 4, 5, 6, kOpPrefix, ots::kCntrMask, 0x00, // vstem
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kCntrMask, 0x00, // no stems to mask
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, kOpPrefix, ots::kHStem,
+ 3, 4, 5, kOpPrefix, ots::kCntrMask, 0x00, // invalid vstem
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestAbs) {
+ {
+ const int char_string[] = {
+ -1, kOpPrefix, ots::kAbs,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kAbs, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestAdd) {
+ {
+ const int char_string[] = {
+ 0, 1, kOpPrefix, ots::kAdd,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kAdd, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestSub) {
+ {
+ const int char_string[] = {
+ 2, 1, kOpPrefix, ots::kSub,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kSub, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestDiv) {
+ // TODO(yusukes): Test div-by-zero.
+ {
+ const int char_string[] = {
+ 2, 1, kOpPrefix, ots::kDiv,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kDiv, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestNeg) {
+ {
+ const int char_string[] = {
+ -1, kOpPrefix, ots::kNeg,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kNeg, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestRandom) {
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kRandom, // OTS rejects the operator.
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestMul) {
+ {
+ const int char_string[] = {
+ 2, 1, kOpPrefix, ots::kMul,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kMul, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestSqrt) {
+ // TODO(yusukes): Test negative numbers.
+ {
+ const int char_string[] = {
+ 4, kOpPrefix, ots::kSqrt,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kSqrt, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestDrop) {
+ {
+ const int char_string[] = {
+ 1, 1, kOpPrefix, ots::kAdd,
+ kOpPrefix, ots::kDrop,
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kDrop, // invalid
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestExch) {
+ {
+ const int char_string[] = {
+ 1, 1, kOpPrefix, ots::kAdd,
+ kOpPrefix, ots::kDup,
+ kOpPrefix, ots::kExch,
+ kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 1, kOpPrefix, ots::kAdd,
+ kOpPrefix, ots::kExch, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestIndex) {
+ {
+ const int char_string[] = {
+ 1, 2, 3, -1, kOpPrefix, ots::kIndex, // OTS rejects the operator.
+ kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestRoll) {
+ {
+ const int char_string[] = {
+ 1, 2, 2, 1, kOpPrefix, ots::kRoll, // OTS rejects the operator.
+ kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestDup) {
+ {
+ const int char_string[] = {
+ 1, 1, kOpPrefix, ots::kAdd,
+ kOpPrefix, ots::kDup,
+ kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kDup, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestPut) {
+ {
+ const int char_string[] = {
+ 1, 10, kOpPrefix, ots::kPut, // OTS rejects the operator.
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestGet) {
+ {
+ const int char_string[] = {
+ 1, 10, kOpPrefix, ots::kGet, // OTS rejects the operator.
+ 1, 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestAnd) {
+ {
+ const int char_string[] = {
+ 2, 1, kOpPrefix, ots::kAnd,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kAnd, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestOr) {
+ {
+ const int char_string[] = {
+ 2, 1, kOpPrefix, ots::kOr,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kOr, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestNot) {
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kNot,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, ots::kNot, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestEq) {
+ {
+ const int char_string[] = {
+ 2, 1, kOpPrefix, ots::kEq,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, kOpPrefix, ots::kEq, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestIfElse) {
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, kOpPrefix, ots::kIfElse,
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, kOpPrefix, ots::kIfElse, // invalid
+ 2, kOpPrefix, ots::kHStem,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestCallSubr) {
+ // Call valid subr.
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ const int local_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+ NULL, 0,
+ local_subrs, ARRAYSIZE(local_subrs)));
+ }
+ // Call undefined subr.
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+ };
+ const int local_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ NULL, 0,
+ local_subrs, ARRAYSIZE(local_subrs)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+ };
+ const int local_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ NULL, 0,
+ local_subrs, ARRAYSIZE(local_subrs)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestCallGSubr) {
+ // Call valid subr.
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+ };
+ const int global_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ NULL, 0));
+ }
+ // Call undefined subr.
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+ };
+ const int global_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ NULL, 0));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+ };
+ const int global_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ NULL, 0));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestCallGSubrWithComputedValues) {
+ {
+ // OTS does not allow to call(g)subr with a subroutine number which is
+ // not a immediate value for safety.
+ const int char_string[] = {
+ 0, 0, kOpPrefix, ots::kAdd,
+ kOpPrefix, ots::kCallGSubr,
+ };
+ const int global_subrs[] = {
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ NULL, 0));
+ }
+}
+
+TEST(ValidateTest, TestInfiniteLoop) {
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ const int local_subrs[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ NULL, 0,
+ local_subrs, ARRAYSIZE(local_subrs)));
+ }
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+ };
+ const int global_subrs[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ NULL, 0));
+ }
+ // mutual recursion which doesn't stop.
+ {
+ const int char_string[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ const int global_subrs[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+ };
+ const int local_subrs[] = {
+ GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+ };
+ EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+ global_subrs, ARRAYSIZE(global_subrs),
+ local_subrs, ARRAYSIZE(local_subrs)));
+ }
+}
+
+TEST(ValidateTest, TestStackOverflow) {
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, // overflow
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestDeprecatedOperators) {
+ {
+ const int char_string[] = {
+ kOpPrefix, 16, // 'blend'.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, (12 << 8) + 8, // 'store'.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ kOpPrefix, (12 << 8) + 13, // 'load'.
+ kOpPrefix, ots::kEndChar,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
+
+TEST(ValidateTest, TestUnterminatedCharString) {
+ // No endchar operator.
+ {
+ const int char_string[] = {
+ 123,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 123, 456,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+ {
+ const int char_string[] = {
+ 123, 456, kOpPrefix, ots::kReturn,
+ };
+ EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+ }
+}
diff --git a/gfx/ots/tests/layout_common_table_test.cc b/gfx/ots/tests/layout_common_table_test.cc
new file mode 100644
index 000000000..91e429741
--- /dev/null
+++ b/gfx/ots/tests/layout_common_table_test.cc
@@ -0,0 +1,770 @@
+// Copyright (c) 2011-2017 The OTS 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 <cmath>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "layout.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+const uint32_t kFakeTag = 0x00000000;
+const size_t kScriptRecordSize = 6;
+const size_t kLangSysRecordSize = 6;
+
+bool BuildFakeScriptListTable(ots::OTSStream *out, const uint16_t script_count,
+ const uint16_t langsys_count,
+ const uint16_t feature_count) {
+ if (!out->WriteU16(script_count)) {
+ return false;
+ }
+ const off_t script_record_end = out->Tell() +
+ kScriptRecordSize * script_count;
+ const size_t script_table_size = 4 + kLangSysRecordSize * langsys_count;
+ for (unsigned i = 0; i < script_count; ++i) {
+ if (!out->WriteU32(kFakeTag) ||
+ !out->WriteU16(script_record_end + i * script_table_size)) {
+ return false;
+ }
+ }
+
+ // Offsets to LangSys tables are measured from the beginning of each
+ // script table.
+ const off_t langsys_record_end = 4 + kLangSysRecordSize * langsys_count;
+ const size_t langsys_table_size = 6 + 2 * feature_count;
+ // Write Fake Script tables.
+ for (unsigned i = 0; i < script_count; ++i) {
+ if (!out->WriteU16(0x0000) ||
+ !out->WriteU16(langsys_count)) {
+ return false;
+ }
+ for (unsigned j = 0; j < langsys_count; ++j) {
+ if (!out->WriteU32(kFakeTag) ||
+ !out->WriteU16(langsys_record_end + j * langsys_table_size)) {
+ return false;
+ }
+ }
+ }
+
+ // Write Fake LangSys tables.
+ for (unsigned i = 0; i < langsys_count; ++i) {
+ if (!out->WriteU16(0x0000) ||
+ !out->WriteU16(0xFFFF) ||
+ !out->WriteU16(feature_count)) {
+ return false;
+ }
+ for (unsigned j = 0; j < feature_count; ++j) {
+ if (!out->WriteU16(j)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+const size_t kFeatureRecordSize = 6;
+
+bool BuildFakeFeatureListTable(ots::OTSStream *out,
+ const uint16_t feature_count,
+ const uint16_t lookup_count) {
+ if (!out->WriteU16(feature_count)) {
+ return false;
+ }
+ const off_t feature_record_end = out->Tell() +
+ kFeatureRecordSize * feature_count;
+ const size_t feature_table_size = 4 + 2 * lookup_count;
+ for (unsigned i = 0; i < feature_count; ++i) {
+ if (!out->WriteU32(kFakeTag) ||
+ !out->WriteU16(feature_record_end + i * feature_table_size)) {
+ return false;
+ }
+ }
+
+ // Write FeatureTable
+ for (unsigned i = 0; i < feature_count; ++i) {
+ if (!out->WriteU16(0x0000) ||
+ !out->WriteU16(lookup_count)) {
+ return false;
+ }
+ for (uint16_t j = 0; j < lookup_count; ++j) {
+ if (!out->WriteU16(j)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool BuildFakeLookupListTable(ots::OTSStream *out, const uint16_t lookup_count,
+ const uint16_t subtable_count) {
+ if (!out->WriteU16(lookup_count)) {
+ return false;
+ }
+ const off_t base_offset_lookup = out->Tell();
+ if (!out->Pad(2 * lookup_count)) {
+ return false;
+ }
+
+ std::vector<off_t> offsets_lookup(lookup_count, 0);
+ for (uint16_t i = 0; i < lookup_count; ++i) {
+ offsets_lookup[i] = out->Tell();
+ if (!out->WriteU16(i + 1) ||
+ !out->WriteU16(0) ||
+ !out->WriteU16(subtable_count) ||
+ !out->Pad(2 * subtable_count) ||
+ !out->WriteU16(0)) {
+ return false;
+ }
+ }
+
+ const off_t offset_lookup_table_end = out->Tell();
+ // Allocate 256 bytes for each subtable.
+ if (!out->Pad(256 * lookup_count * subtable_count)) {
+ return false;
+ }
+
+ if (!out->Seek(base_offset_lookup)) {
+ return false;
+ }
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!out->WriteU16(offsets_lookup[i])) {
+ return false;
+ }
+ }
+
+ for (unsigned i = 0; i < lookup_count; ++i) {
+ if (!out->Seek(offsets_lookup[i] + 6)) {
+ return false;
+ }
+ for (unsigned j = 0; j < subtable_count; ++j) {
+ if (!out->WriteU16(offset_lookup_table_end +
+ 256*i*subtable_count + 256*j)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool BuildFakeCoverageFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+ if (!out->WriteU16(1) || !out->WriteU16(glyph_count)) {
+ return false;
+ }
+ for (uint16_t glyph_id = 1; glyph_id <= glyph_count; ++glyph_id) {
+ if (!out->WriteU16(glyph_id)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BuildFakeCoverageFormat2(ots::OTSStream *out, const uint16_t range_count) {
+ if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+ return false;
+ }
+ uint16_t glyph_id = 1;
+ uint16_t start_coverage_index = 0;
+ for (unsigned i = 0; i < range_count; ++i) {
+ // Write consecutive ranges in which each range consists of two glyph id.
+ if (!out->WriteU16(glyph_id) ||
+ !out->WriteU16(glyph_id + 1) ||
+ !out->WriteU16(start_coverage_index)) {
+ return false;
+ }
+ glyph_id += 2;
+ start_coverage_index += 2;
+ }
+ return true;
+}
+
+bool BuildFakeClassDefFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+ if (!out->WriteU16(1) ||
+ !out->WriteU16(1) ||
+ !out->WriteU16(glyph_count)) {
+ return false;
+ }
+ for (uint16_t class_value = 1; class_value <= glyph_count; ++class_value) {
+ if (!out->WriteU16(class_value)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BuildFakeClassDefFormat2(ots::OTSStream *out, const uint16_t range_count) {
+ if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+ return false;
+ }
+ uint16_t glyph_id = 1;
+ for (uint16_t class_value = 1; class_value <= range_count; ++class_value) {
+ // Write consecutive ranges in which each range consists of one glyph id.
+ if (!out->WriteU16(glyph_id) ||
+ !out->WriteU16(glyph_id + 1) ||
+ !out->WriteU16(class_value)) {
+ return false;
+ }
+ glyph_id += 2;
+ }
+ return true;
+}
+
+bool BuildFakeDeviceTable(ots::OTSStream *out, const uint16_t start_size,
+ const uint16_t end_size, const uint16_t format) {
+ if (!out->WriteU16(start_size) ||
+ !out->WriteU16(end_size) ||
+ !out->WriteU16(format)) {
+ return false;
+ }
+
+ const unsigned num_values = std::abs(end_size - start_size) + 1;
+ const unsigned num_bits = (1 << format) * num_values;
+ const unsigned num_units = (num_bits - 1) / 16 + 1;
+ if (!out->Pad(num_units * 2)) {
+ return false;
+ }
+ return true;
+}
+
+class TestStream : public ots::MemoryStream {
+ public:
+ TestStream()
+ : ots::MemoryStream(data_, sizeof(data_)), size_(0) {
+ std::memset(reinterpret_cast<char*>(data_), 0, sizeof(data_));
+ }
+
+ uint8_t* data() { return data_; }
+ size_t size() const { return size_; }
+
+ virtual bool WriteRaw(const void *d, size_t length) {
+ if (Tell() + length > size_) {
+ size_ = Tell() + length;
+ }
+ return ots::MemoryStream::WriteRaw(d, length);
+ }
+
+ private:
+ size_t size_;
+ uint8_t data_[4096];
+};
+
+class TableTest : public ::testing::Test {
+ protected:
+
+ virtual void SetUp() {
+ ots::FontFile *file = new ots::FontFile();
+ file->context = new ots::OTSContext();
+ font = new ots::Font(file);
+ }
+
+ virtual void TearDown() {
+ delete font->file->context;
+ delete font->file;
+ delete font;
+ }
+
+ TestStream out;
+ ots::Font *font;
+};
+
+class ScriptListTableTest : public TableTest { };
+class DeviceTableTest : public TableTest { };
+class CoverageTableTest : public TableTest { };
+class CoverageFormat1Test : public TableTest { };
+class CoverageFormat2Test : public TableTest { };
+class ClassDefTableTest : public TableTest { };
+class ClassDefFormat1Test : public TableTest { };
+class ClassDefFormat2Test : public TableTest { };
+class LookupSubtableParserTest : public TableTest { };
+
+class FeatureListTableTest : public TableTest {
+ protected:
+
+ virtual void SetUp() {
+ TableTest::SetUp();
+ num_features = 0;
+ }
+
+ uint16_t num_features;
+};
+
+bool fakeTypeParserReturnsTrue(const ots::Font*, const uint8_t *,
+ const size_t) {
+ return true;
+}
+
+bool fakeTypeParserReturnsFalse(const ots::Font*, const uint8_t *,
+ const size_t) {
+ return false;
+}
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnTrue[] = {
+ {1, fakeTypeParserReturnsTrue},
+ {2, fakeTypeParserReturnsTrue},
+ {3, fakeTypeParserReturnsTrue},
+ {4, fakeTypeParserReturnsTrue},
+ {5, fakeTypeParserReturnsTrue}
+};
+
+// Fake lookup subtable parser which always returns true.
+const ots::LookupSubtableParser FakeLookupParserReturnsTrue = {
+ 5, 5, TypeParsersReturnTrue,
+};
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnFalse[] = {
+ {1, fakeTypeParserReturnsFalse}
+};
+
+// Fake lookup subtable parser which always returns false.
+const ots::LookupSubtableParser FakeLookupParserReturnsFalse = {
+ 1, 1, TypeParsersReturnFalse
+};
+
+class LookupListTableTest : public TableTest {
+ protected:
+
+ virtual void SetUp() {
+ TableTest::SetUp();
+ num_lookups = 0;
+ }
+
+ bool Parse() {
+ return ots::ParseLookupListTable(font, out.data(), out.size(),
+ &FakeLookupParserReturnsTrue,
+ &num_lookups);
+ }
+
+ uint16_t num_lookups;
+};
+
+} // namespace
+
+TEST_F(ScriptListTableTest, TestSuccess) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ EXPECT_TRUE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadScriptCount) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set too large script count.
+ out.Seek(0);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetUnderflow) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set bad offset to ScriptRecord[0].
+ out.Seek(6);
+ out.WriteU16(0);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetOverflow) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set bad offset to ScriptRecord[0].
+ out.Seek(6);
+ out.WriteU16(out.size());
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadLangSysCount) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set too large langsys count.
+ out.Seek(10);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetUnderflow) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set bad offset to LangSysRecord[0].
+ out.Seek(16);
+ out.WriteU16(0);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetOverflow) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set bad offset to LangSysRecord[0].
+ out.Seek(16);
+ out.WriteU16(out.size());
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadReqFeatureIndex) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set too large feature index to ReqFeatureIndex of LangSysTable[0].
+ out.Seek(20);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureCount) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set too large feature count to LangSysTable[0].
+ out.Seek(22);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureIndex) {
+ BuildFakeScriptListTable(&out, 1, 1, 1);
+ // Set too large feature index to ReatureIndex[0] of LangSysTable[0].
+ out.Seek(24);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseScriptListTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(FeatureListTableTest, TestSuccess) {
+ BuildFakeFeatureListTable(&out, 1, 1);
+ EXPECT_TRUE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
+ &num_features));
+ EXPECT_EQ(num_features, 1);
+}
+
+TEST_F(FeatureListTableTest, TestSuccess2) {
+ BuildFakeFeatureListTable(&out, 5, 1);
+ EXPECT_TRUE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
+ &num_features));
+ EXPECT_EQ(num_features, 5);
+}
+
+TEST_F(FeatureListTableTest, TestBadFeatureCount) {
+ BuildFakeFeatureListTable(&out, 1, 1);
+ // Set too large feature count.
+ out.Seek(0);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
+ &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureUnderflow) {
+ BuildFakeFeatureListTable(&out, 1, 1);
+ // Set bad offset to FeatureRecord[0].
+ out.Seek(6);
+ out.WriteU16(0);
+ EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
+ &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureOverflow) {
+ BuildFakeFeatureListTable(&out, 1, 1);
+ // Set bad offset to FeatureRecord[0].
+ out.Seek(6);
+ out.WriteU16(out.size());
+ EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
+ &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestBadLookupCount) {
+ BuildFakeFeatureListTable(&out, 1, 1);
+ // Set too large lookup count to FeatureTable[0].
+ out.Seek(10);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseFeatureListTable(font, out.data(), out.size(), 1,
+ &num_features));
+}
+
+TEST_F(LookupListTableTest, TestSuccess) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ EXPECT_TRUE(Parse());
+ EXPECT_EQ(num_lookups, 1);
+}
+
+TEST_F(LookupListTableTest, TestSuccess2) {
+ BuildFakeLookupListTable(&out, 5, 1);
+ EXPECT_TRUE(Parse());
+ EXPECT_EQ(num_lookups, 5);
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableUnderflow) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set bad offset to Lookup[0].
+ out.Seek(2);
+ out.WriteU16(0);
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableOverflow) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set bad offset to Lookup[0].
+ out.Seek(2);
+ out.WriteU16(out.size());
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableUnderflow) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set bad offset to SubTable[0] of LookupTable[0].
+ out.Seek(10);
+ out.WriteU16(0);
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableOverflow) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set bad offset to SubTable[0] of LookupTable[0].
+ out.Seek(10);
+ out.WriteU16(out.size());
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupCount) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set too large lookup count of LookupTable[0].
+ out.Seek(0);
+ out.WriteU16(2);
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupType) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set too large lookup type of LookupTable[0].
+ out.Seek(4);
+ out.WriteU16(6);
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupFlag) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
+ out.Seek(6);
+ out.WriteU16(0x0002);
+ EXPECT_TRUE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadSubtableCount) {
+ BuildFakeLookupListTable(&out, 1, 1);
+ // Set too large sutable count of LookupTable[0].
+ out.Seek(8);
+ out.WriteU16(2);
+ EXPECT_FALSE(Parse());
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat1) {
+ BuildFakeCoverageFormat1(&out, 1);
+ EXPECT_TRUE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat2) {
+ BuildFakeCoverageFormat2(&out, 1);
+ EXPECT_TRUE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestBadFormat) {
+ BuildFakeCoverageFormat1(&out, 1);
+ // Set bad format.
+ out.Seek(0);
+ out.WriteU16(3);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphCount) {
+ BuildFakeCoverageFormat1(&out, 1);
+ // Set too large glyph count.
+ out.Seek(2);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphId) {
+ BuildFakeCoverageFormat1(&out, 1);
+ // Set too large glyph id.
+ out.Seek(4);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRangeCount) {
+ BuildFakeCoverageFormat2(&out, 1);
+ // Set too large range count.
+ out.Seek(2);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRange) {
+ BuildFakeCoverageFormat2(&out, 1);
+ // Set reverse order glyph id to start/end fields.
+ out.Seek(4);
+ out.WriteU16(2);
+ out.WriteU16(1);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap) {
+ BuildFakeCoverageFormat2(&out, 2);
+ // Set overlapping glyph id to an end field.
+ out.Seek(12);
+ out.WriteU16(1);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 2));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap2) {
+ BuildFakeCoverageFormat2(&out, 2);
+ // Set overlapping range.
+ out.Seek(10);
+ out.WriteU16(1);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseCoverageTable(font, out.data(), out.size(), 2));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat1) {
+ BuildFakeClassDefFormat1(&out, 1);
+ EXPECT_TRUE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat2) {
+ BuildFakeClassDefFormat2(&out, 1);
+ EXPECT_TRUE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestBadFormat) {
+ BuildFakeClassDefFormat1(&out, 1);
+ // Set bad format.
+ out.Seek(0);
+ out.WriteU16(3);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadStartGlyph) {
+ BuildFakeClassDefFormat1(&out, 1);
+ // Set too large start glyph id.
+ out.Seek(2);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadGlyphCount) {
+ BuildFakeClassDefFormat1(&out, 1);
+ // Set too large glyph count.
+ out.Seek(4);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadClassValue) {
+ BuildFakeClassDefFormat1(&out, 1);
+ // Set too large class value.
+ out.Seek(6);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestBadRangeCount) {
+ BuildFakeClassDefFormat2(&out, 1);
+ // Set too large range count.
+ out.Seek(2);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap) {
+ BuildFakeClassDefFormat2(&out, 2);
+ // Set overlapping glyph id to an end field.
+ out.Seek(12);
+ out.WriteU16(1);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap2) {
+ BuildFakeClassDefFormat2(&out, 2);
+ // Set overlapping range.
+ out.Seek(10);
+ out.WriteU16(1);
+ out.WriteU16(2);
+ EXPECT_FALSE(ots::ParseClassDefTable(font, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success) {
+ BuildFakeDeviceTable(&out, 1, 8, 1);
+ EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success2) {
+ BuildFakeDeviceTable(&out, 1, 9, 1);
+ EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail) {
+ // Pass shorter length than expected.
+ BuildFakeDeviceTable(&out, 1, 8, 1);
+ EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail2) {
+ // Pass shorter length than expected.
+ BuildFakeDeviceTable(&out, 1, 9, 1);
+ EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success) {
+ BuildFakeDeviceTable(&out, 1, 1, 2);
+ EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success2) {
+ BuildFakeDeviceTable(&out, 1, 8, 2);
+ EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail) {
+ // Pass shorter length than expected.
+ BuildFakeDeviceTable(&out, 1, 8, 2);
+ EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail2) {
+ // Pass shorter length than expected.
+ BuildFakeDeviceTable(&out, 1, 9, 2);
+ EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success) {
+ BuildFakeDeviceTable(&out, 1, 1, 3);
+ EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success2) {
+ BuildFakeDeviceTable(&out, 1, 8, 3);
+ EXPECT_TRUE(ots::ParseDeviceTable(font, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail) {
+ // Pass shorter length than expected.
+ BuildFakeDeviceTable(&out, 1, 8, 3);
+ EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail2) {
+ // Pass shorter length than expected.
+ BuildFakeDeviceTable(&out, 1, 9, 3);
+ EXPECT_FALSE(ots::ParseDeviceTable(font, out.data(), out.size() - 1));
+}
+
+TEST_F(LookupSubtableParserTest, TestSuccess) {
+ {
+ EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 1));
+ }
+ {
+ EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 5));
+ }
+}
+
+TEST_F(LookupSubtableParserTest, TestFail) {
+ {
+ // Pass bad lookup type which less than the smallest type.
+ EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 0));
+ }
+ {
+ // Pass bad lookup type which greater than the maximum type.
+ EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(font, 0, 0, 6));
+ }
+ {
+ // Check the type parser failure.
+ EXPECT_FALSE(FakeLookupParserReturnsFalse.Parse(font, 0, 0, 1));
+ }
+}