Line data Source code
1 : // Unit tests for the numeric-edges semantic-executor functions.
2 : //
3 : // These tests bypass the analyzer and call the per-function helpers
4 : // directly with hand-built `Value` arguments. End-to-end coverage
5 : // (analyzer -> dispatch -> result) lives in `dispatch_test.cc`.
6 :
7 : #include "backend/engine/semantic/functions/numeric_edges.h"
8 :
9 : #include <cmath>
10 : #include <cstdint>
11 : #include <limits>
12 : #include <vector>
13 :
14 : #include "absl/status/status.h"
15 : #include "absl/status/statusor.h"
16 : #include "backend/engine/semantic/error.h"
17 : #include "backend/engine/semantic/value.h"
18 : #include "googlesql/public/type.h"
19 : #include "googlesql/public/value.h"
20 : #include "gtest/gtest.h"
21 :
22 : namespace bigquery_emulator {
23 : namespace backend {
24 : namespace engine {
25 : namespace semantic {
26 : namespace functions {
27 : namespace {
28 :
29 1 : TEST(BitCountTest, ZeroHasNoSetBits) {
30 1 : auto v = BitCount({Value::Int64(0)});
31 2 : ASSERT_TRUE(v.ok()) << v.status();
32 1 : EXPECT_EQ(v->int64_value(), 0);
33 1 : }
34 :
35 1 : TEST(BitCountTest, SmallPositive) {
36 1 : auto v = BitCount({Value::Int64(7)}); // 0b111
37 2 : ASSERT_TRUE(v.ok()) << v.status();
38 1 : EXPECT_EQ(v->int64_value(), 3);
39 1 : }
40 :
41 1 : TEST(BitCountTest, NegativeOneAllBitsSet) {
42 : // -1 in two's-complement has every bit set; BigQuery returns 64.
43 : // This is the key correctness case the row exists to pin: a
44 : // thin macro on DuckDB's unsigned `bit_count` cannot match this
45 : // without re-interpreting the bit pattern.
46 1 : auto v = BitCount({Value::Int64(-1)});
47 2 : ASSERT_TRUE(v.ok()) << v.status();
48 1 : EXPECT_EQ(v->int64_value(), 64);
49 1 : }
50 :
51 1 : TEST(BitCountTest, Int64MinSignBitOnly) {
52 1 : auto v = BitCount({Value::Int64(std::numeric_limits<int64_t>::min())});
53 2 : ASSERT_TRUE(v.ok()) << v.status();
54 1 : EXPECT_EQ(v->int64_value(), 1);
55 1 : }
56 :
57 1 : TEST(BitCountTest, NullPropagates) {
58 1 : auto v = BitCount({Value::NullInt64()});
59 2 : ASSERT_TRUE(v.ok()) << v.status();
60 1 : EXPECT_TRUE(v->is_null());
61 1 : }
62 :
63 1 : TEST(BitCountTest, NonInt64Rejected) {
64 1 : Value arg = Value::String("hi");
65 1 : auto v = BitCount({arg});
66 1 : ASSERT_FALSE(v.ok());
67 1 : EXPECT_EQ(GetSemanticErrorReason(v.status()),
68 1 : SemanticErrorReason::kInvalidArgument);
69 1 : }
70 :
71 1 : TEST(BitCountTest, ArityMismatchRejected) {
72 1 : auto v = BitCount({Value::Int64(1), Value::Int64(2)});
73 1 : ASSERT_FALSE(v.ok());
74 1 : EXPECT_EQ(v.status().code(), absl::StatusCode::kInvalidArgument);
75 1 : }
76 :
77 1 : TEST(IeeeDivideTest, FinitePath) {
78 1 : auto v = IeeeDivide({Value::Double(10.0), Value::Double(4.0)});
79 2 : ASSERT_TRUE(v.ok()) << v.status();
80 1 : EXPECT_DOUBLE_EQ(v->double_value(), 2.5);
81 1 : }
82 :
83 1 : TEST(IeeeDivideTest, DivisionByZeroProducesInf) {
84 : // IEEE_DIVIDE diverges from SAFE_DIVIDE and `/` here: SAFE_DIVIDE
85 : // returns NULL, `/` raises, IEEE_DIVIDE returns +/-Inf per IEEE 754.
86 1 : auto v = IeeeDivide({Value::Double(1.0), Value::Double(0.0)});
87 2 : ASSERT_TRUE(v.ok()) << v.status();
88 1 : EXPECT_TRUE(std::isinf(v->double_value()));
89 1 : EXPECT_GT(v->double_value(), 0.0);
90 1 : }
91 :
92 1 : TEST(IeeeDivideTest, NegativeOverZeroProducesNegativeInf) {
93 1 : auto v = IeeeDivide({Value::Double(-1.0), Value::Double(0.0)});
94 2 : ASSERT_TRUE(v.ok()) << v.status();
95 1 : EXPECT_TRUE(std::isinf(v->double_value()));
96 1 : EXPECT_LT(v->double_value(), 0.0);
97 1 : }
98 :
99 1 : TEST(IeeeDivideTest, ZeroOverZeroProducesNan) {
100 1 : auto v = IeeeDivide({Value::Double(0.0), Value::Double(0.0)});
101 2 : ASSERT_TRUE(v.ok()) << v.status();
102 1 : EXPECT_TRUE(std::isnan(v->double_value()));
103 1 : }
104 :
105 1 : TEST(IeeeDivideTest, NullPropagates) {
106 1 : auto v = IeeeDivide({Value::NullDouble(), Value::Double(1.0)});
107 2 : ASSERT_TRUE(v.ok()) << v.status();
108 1 : EXPECT_TRUE(v->is_null());
109 1 : }
110 :
111 1 : TEST(IeeeDivideTest, NonDoubleRejected) {
112 1 : auto v = IeeeDivide({Value::Int64(1), Value::Int64(2)});
113 1 : ASSERT_FALSE(v.ok());
114 1 : EXPECT_EQ(GetSemanticErrorReason(v.status()),
115 1 : SemanticErrorReason::kInvalidArgument);
116 1 : }
117 :
118 : } // namespace
119 : } // namespace functions
120 : } // namespace semantic
121 : } // namespace engine
122 : } // namespace backend
123 : } // namespace bigquery_emulator
|