| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "paimon/common/data/variant/variant_json_utils.h" |
| |
| #include <cmath> |
| #include <limits> |
| #include <string> |
| |
| #include "gtest/gtest.h" |
| #include "paimon/testing/utils/testharness.h" |
| |
| namespace paimon::test { |
| |
| TEST(VariantJsonUtilsTest, JavaDoubleToString) { |
| // Mirrors java.lang.Double#toString exactly. |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(0.0), "0.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(-0.0), "-0.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(std::numeric_limits<double>::infinity()), |
| "Infinity"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(-std::numeric_limits<double>::infinity()), |
| "-Infinity"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(std::nan("")), "NaN"); |
| // Double.MIN_VALUE (the smallest positive subnormal) and Double.MAX_VALUE. |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(std::numeric_limits<double>::denorm_min()), |
| "4.9E-324"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(std::numeric_limits<double>::max()), |
| "1.7976931348623157E308"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(std::numeric_limits<double>::min()), |
| "2.2250738585072014E-308"); |
| |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(1.0), "1.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(-1.0), "-1.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(-0.001), "-0.001"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(1e7), "1.0E7"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(1234567.0), "1234567.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(0.001), "0.001"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(1.0E-3), "0.001"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(1.0E-4), "1.0E-4"); |
| EXPECT_EQ(VariantJsonUtils::JavaDoubleToString(0.1 + 0.2), "0.30000000000000004"); |
| } |
| |
| TEST(VariantJsonUtilsTest, JavaFloatToString) { |
| // Mirrors java.lang.Float#toString exactly. |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(0.0F), "0.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(-0.0F), "-0.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(std::numeric_limits<float>::infinity()), |
| "Infinity"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(-std::numeric_limits<float>::infinity()), |
| "-Infinity"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(std::nanf("")), "NaN"); |
| // Float.MIN_VALUE (the smallest positive subnormal) and Float.MAX_VALUE. |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(std::numeric_limits<float>::denorm_min()), |
| "1.4E-45"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(std::numeric_limits<float>::max()), |
| "3.4028235E38"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(1.0F), "1.0"); |
| EXPECT_EQ(VariantJsonUtils::JavaFloatToString(2.5F), "2.5"); |
| } |
| |
| TEST(VariantJsonUtilsTest, AppendEscapedJsonControlChars) { |
| // Control characters without a named escape are emitted as \uXXXX. |
| std::string out; |
| VariantJsonUtils::AppendEscapedJson(std::string_view("\x01\x1f", 2), &out); |
| EXPECT_EQ(out, "\"\\u0001\\u001f\""); |
| } |
| |
| TEST(VariantJsonUtilsTest, DateToStringNegativeYear) { |
| // A date far before the epoch renders a negative (BCE) year with a leading '-'. |
| std::string result = VariantJsonUtils::DateToString(-800000); |
| ASSERT_FALSE(result.empty()); |
| EXPECT_EQ(result.front(), '-'); |
| } |
| |
| TEST(VariantJsonUtilsTest, TimestampToStringEdgeCases) { |
| // Sub-second fraction trailing zeros are trimmed. |
| EXPECT_EQ(VariantJsonUtils::TimestampToString(500000, 0, /*with_offset=*/false), |
| "1970-01-01 00:00:00.5"); |
| // Negative epoch micros floor to the previous day (FloorDiv keeps a non-negative remainder). |
| EXPECT_EQ(VariantJsonUtils::TimestampToString(-1, 0, /*with_offset=*/false), |
| "1969-12-31 23:59:59.999999"); |
| // A positive zone offset is appended as +HH:MM. |
| EXPECT_EQ(VariantJsonUtils::TimestampToString(0, 8 * 3600, /*with_offset=*/true), |
| "1970-01-01 08:00:00+08:00"); |
| } |
| |
| TEST(VariantJsonUtilsTest, ZoneOffsetParsing) { |
| // `UTC`/`GMT`/`UT` prefixes are stripped before the fixed offset is parsed. |
| ASSERT_OK_AND_ASSIGN(int32_t utc_prefixed, |
| VariantJsonUtils::GetZoneOffsetSeconds("UTC+08:00", 0)); |
| EXPECT_EQ(utc_prefixed, 8 * 3600); |
| ASSERT_OK_AND_ASSIGN(int32_t ut_prefixed, VariantJsonUtils::GetZoneOffsetSeconds("UT+05", 0)); |
| EXPECT_EQ(ut_prefixed, 5 * 3600); |
| // HHMMSS form and a negative offset. |
| ASSERT_OK_AND_ASSIGN(int32_t hms, VariantJsonUtils::GetZoneOffsetSeconds("+18:30:15", 0)); |
| EXPECT_EQ(hms, 18 * 3600 + 30 * 60 + 15); |
| ASSERT_OK_AND_ASSIGN(int32_t neg, VariantJsonUtils::GetZoneOffsetSeconds("-06:30", 0)); |
| EXPECT_EQ(neg, -(6 * 3600 + 30 * 60)); |
| // Invalid forms are rejected: a non-digit char, a wrong digit count, and an out-of-range hour. |
| ASSERT_NOK(VariantJsonUtils::GetZoneOffsetSeconds("+9A", 0)); |
| ASSERT_NOK(VariantJsonUtils::GetZoneOffsetSeconds("+123", 0)); |
| ASSERT_NOK(VariantJsonUtils::GetZoneOffsetSeconds("+19:00", 0)); |
| } |
| |
| } // namespace paimon::test |