blob: d2cf513e13a9a3907ed14633fc1df115b584a65d [file]
/*
* 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