[mathml] Add unit tests for OpenTypeMathSupport

A platform API for the OpenType MATH table has been added in [1] but
the code is not verified by blink_platform_unittests. This CL adds a
few tests for fonts with or without a MATH table as well as constants
related to script scale down, fractions and radicals. This is not
exhaustive but at least cover all the return statements of
OpenTypeMathSupport::HasMathData and OpenTypeMathSupport::MathConstant.

This also modifies the result of OpenTypeMathSupport::MathConstant
for fonts without a MATH table: Instead of returning HarfBuzz's zero
value, the function returns a null optional so that callers can decide
the default value to use.

[1] https://chromium-review.googlesource.com/c/chromium/src/+/2041485

Bug: 6606, 1050596
Change-Id: I4e7f329bbabb1759f6a1adeaa74b49912812ba8e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2073721
Commit-Queue: Frédéric Wang <fwang@igalia.com>
Reviewed-by: Dominik Röttsches <drott@chromium.org>
Cr-Commit-Position: refs/heads/master@{#744982}
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index f65dbbc..3913841 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1761,6 +1761,7 @@
     "fonts/generic_font_family_settings_test.cc",
     "fonts/mac/font_matcher_mac_test.mm",
     "fonts/opentype/font_settings_test.cc",
+    "fonts/opentype/open_type_math_support_test.cc",
     "fonts/opentype/open_type_vertical_data_test.cc",
     "fonts/orientation_iterator_test.cc",
     "fonts/script_run_iterator_test.cc",
@@ -2035,6 +2036,9 @@
     # Required by some image decoder tests.
     "image-decoders/testing/",
     "//third_party/blink/web_tests/images/resources/",
+
+    # Required by some font tests.
+    "//third_party/blink/web_tests/external/wpt/fonts/",
   ]
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc b/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc
index 6debef8..e90a002b 100644
--- a/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc
@@ -38,7 +38,7 @@
 base::Optional<float> OpenTypeMathSupport::MathConstant(
     const HarfBuzzFace* harfbuzz_face,
     MathConstants constant) {
-  if (!harfbuzz_face)
+  if (!HasMathData(harfbuzz_face))
     return base::nullopt;
 
   hb_font_t* font =
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc b/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc
new file mode 100644
index 0000000..bdc468cf
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc
@@ -0,0 +1,238 @@
+// Copyright 2020 The Chromium 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 "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h"
+#include "base/memory/scoped_refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/opentype/open_type_types.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace blink {
+
+class OpenTypeMathSupportTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    font_description.SetComputedSize(10.0);
+    font = Font(font_description);
+    font.Update(nullptr);
+  }
+
+  void TearDown() override {}
+
+  Font CreateMathFont(const String& name, float size = 1000) {
+    FontDescription::VariantLigatures ligatures;
+    return blink::test::CreateTestFont(
+        "MathTestFont",
+        blink::test::BlinkWebTestsFontsTestDataPath(String("math/") + name),
+        size, &ligatures);
+  }
+
+  bool HasMathData(const String& name) {
+    return OpenTypeMathSupport::HasMathData(
+        CreateMathFont(name).PrimaryFont()->PlatformData().GetHarfBuzzFace());
+  }
+
+  base::Optional<float> MathConstant(
+      const String& name,
+      OpenTypeMathSupport::MathConstants constant) {
+    Font math = CreateMathFont(name);
+    return OpenTypeMathSupport::MathConstant(
+        math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), constant);
+  }
+
+  FontDescription font_description;
+  Font font;
+};
+
+TEST_F(OpenTypeMathSupportTest, HasMathData) {
+  // Null parameter.
+  EXPECT_FALSE(OpenTypeMathSupport::HasMathData(nullptr));
+
+  // Font without a MATH table.
+  EXPECT_FALSE(HasMathData("math-text.woff"));
+
+  // Font with a MATH table.
+  EXPECT_TRUE(HasMathData("axisheight5000-verticalarrow14000.woff"));
+}
+
+TEST_F(OpenTypeMathSupportTest, MathConstantNullOpt) {
+  Font math_text = CreateMathFont("math-text.woff");
+
+  for (int i = OpenTypeMathSupport::MathConstants::kScriptPercentScaleDown;
+       i <=
+       OpenTypeMathSupport::MathConstants::kRadicalDegreeBottomRaisePercent;
+       i++) {
+    auto math_constant = static_cast<OpenTypeMathSupport::MathConstants>(i);
+
+    // Null parameter.
+    EXPECT_FALSE(OpenTypeMathSupport::MathConstant(nullptr, math_constant));
+
+    // Font without a MATH table.
+    EXPECT_FALSE(OpenTypeMathSupport::MathConstant(
+        math_text.PrimaryFont()->PlatformData().GetHarfBuzzFace(),
+        math_constant));
+  }
+}
+
+// See third_party/blink/web_tests/external/wpt/mathml/tools/percentscaledown.py
+TEST_F(OpenTypeMathSupportTest, MathConstantPercentScaleDown) {
+  {
+    auto result = MathConstant(
+        "scriptpercentscaledown80-scriptscriptpercentscaledown0.woff",
+        OpenTypeMathSupport::MathConstants::kScriptPercentScaleDown);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, .8);
+  }
+
+  {
+    auto result = MathConstant(
+        "scriptpercentscaledown0-scriptscriptpercentscaledown40.woff",
+        OpenTypeMathSupport::MathConstants::kScriptScriptPercentScaleDown);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, .4);
+  }
+}
+
+// See third_party/blink/web_tests/external/wpt/mathml/tools/fractions.py
+TEST_F(OpenTypeMathSupportTest, MathConstantFractions) {
+  {
+    auto result = MathConstant(
+        "fraction-numeratorshiftup11000-axisheight1000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kFractionNumeratorShiftUp);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 11000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-numeratordisplaystyleshiftup2000-axisheight1000-"
+        "rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::
+            kFractionNumeratorDisplayStyleShiftUp);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 2000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-denominatorshiftdown3000-axisheight1000-rulethickness1000."
+        "woff",
+        OpenTypeMathSupport::MathConstants::kFractionDenominatorShiftDown);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 3000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-denominatordisplaystyleshiftdown6000-axisheight1000-"
+        "rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::
+            kFractionDenominatorDisplayStyleShiftDown);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 6000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-numeratorgapmin9000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kFractionNumeratorGapMin);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 9000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kFractionNumDisplayStyleGapMin);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 8000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-rulethickness10000.woff",
+        OpenTypeMathSupport::MathConstants::kFractionRuleThickness);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 10000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-denominatorgapmin4000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kFractionDenominatorGapMin);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 4000);
+  }
+
+  {
+    auto result = MathConstant(
+        "fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kFractionDenomDisplayStyleGapMin);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 5000);
+  }
+}
+
+// See third_party/blink/web_tests/external/wpt/mathml/tools/radicals.py
+TEST_F(OpenTypeMathSupportTest, MathConstantRadicals) {
+  {
+    auto result = MathConstant(
+        "radical-degreebottomraisepercent25-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kRadicalDegreeBottomRaisePercent);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, .25);
+  }
+
+  {
+    auto result =
+        MathConstant("radical-verticalgap6000-rulethickness1000.woff",
+                     OpenTypeMathSupport::MathConstants::kRadicalVerticalGap);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 6000);
+  }
+
+  {
+    auto result = MathConstant(
+        "radical-displaystyleverticalgap7000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kRadicalDisplayStyleVerticalGap);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 7000);
+  }
+
+  {
+    auto result =
+        MathConstant("radical-rulethickness8000.woff",
+                     OpenTypeMathSupport::MathConstants::kRadicalRuleThickness);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 8000);
+  }
+
+  {
+    auto result =
+        MathConstant("radical-extraascender3000-rulethickness1000.woff",
+                     OpenTypeMathSupport::MathConstants::kRadicalExtraAscender);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 3000);
+  }
+
+  {
+    auto result = MathConstant(
+        "radical-kernbeforedegree4000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kRadicalKernBeforeDegree);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, 4000);
+  }
+
+  {
+    auto result = MathConstant(
+        "radical-kernafterdegreeminus5000-rulethickness1000.woff",
+        OpenTypeMathSupport::MathConstants::kRadicalKernAfterDegree);
+    EXPECT_TRUE(result);
+    EXPECT_FLOAT_EQ(*result, -5000);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.cc b/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
index adb0162..1215543 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
@@ -131,6 +131,13 @@
   return SharedBuffer::Create(buffer.data(), buffer.size());
 }
 
+String BlinkWebTestsFontsTestDataPath(const String& relative_path) {
+  return FilePathToWebString(
+      WebTestsFilePath()
+          .Append(FILE_PATH_LITERAL("external/wpt/fonts"))
+          .Append(WebStringToFilePath(relative_path)));
+}
+
 LineReader::LineReader(const String& text) : text_(text), index_(0) {}
 
 bool LineReader::GetNextLine(String* line) {
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
index 87d7159..a964623 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
@@ -75,6 +75,12 @@
 // specified.
 String AccessibilityTestDataPath(const String& relative_path = String());
 
+// Returns Blink web_tests fonts as an absolute path, i.e.
+// <blinkRootDir>/src/third_party/blink/web_tests/external/wpt/fonts/<relative_path>.
+// It returns the top fonts test directory if |relative_path| was not
+// specified.
+String BlinkWebTestsFontsTestDataPath(const String& relative_path = String());
+
 scoped_refptr<SharedBuffer> ReadFromFile(const String& path);
 
 class LineReader {