diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-11-14 10:07:01 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-11-14 10:07:01 +0100 |
commit | 0f8691a48869932cd3de5195f5211c25e4691b21 (patch) | |
tree | 829675d64c457be0b447dfbcf9534cc22f83d392 /gfx/ots/tests | |
parent | 36975f3865948f3faa959fe386e58b22783bd379 (diff) | |
download | UXP-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.cc | 1588 | ||||
-rw-r--r-- | gfx/ots/tests/layout_common_table_test.cc | 770 |
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)); + } +} |