Skip to content

Commit e72912c

Browse files
authored
chore(tesseract): Tesseract unit tests (#10136)
1 parent 6384cb1 commit e72912c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+7802
-33
lines changed

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/base_tools.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use super::base_query_options::FilterItem;
21
use super::driver_tools::{DriverTools, NativeDriverTools};
3-
use super::filter_group::{FilterGroup, NativeFilterGroup};
4-
use super::filter_params::{FilterParams, NativeFilterParams};
52
use super::join_definition::{JoinDefinition, NativeJoinDefinition};
63
use super::pre_aggregation_obj::{NativePreAggregationObj, PreAggregationObj};
74
use super::security_context::{NativeSecurityContext, SecurityContext};
@@ -23,14 +20,6 @@ pub trait BaseTools {
2320
fn sql_templates(&self) -> Result<Rc<dyn SqlTemplatesRender>, CubeError>;
2421
fn security_context_for_rust(&self) -> Result<Rc<dyn SecurityContext>, CubeError>;
2522
fn sql_utils_for_rust(&self) -> Result<Rc<dyn SqlUtils>, CubeError>;
26-
fn filters_proxy_for_rust(
27-
&self,
28-
used_filters: Option<Vec<FilterItem>>,
29-
) -> Result<Rc<dyn FilterParams>, CubeError>;
30-
fn filter_group_function_for_rust(
31-
&self,
32-
used_filters: Option<Vec<FilterItem>>,
33-
) -> Result<Rc<dyn FilterGroup>, CubeError>;
3423
fn generate_time_series(
3524
&self,
3625
granularity: String,

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/dimension_definition.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub struct DimensionDefinitionStatic {
2828
#[serde(rename = "propagateFiltersToSubQuery")]
2929
pub propagate_filters_to_sub_query: Option<bool>,
3030
pub values: Option<Vec<String>>,
31+
#[serde(rename = "primaryKey")]
32+
pub primary_key: Option<bool>,
3133
}
3234

3335
#[nativebridge::native_bridge(DimensionDefinitionStatic)]

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/evaluator.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ pub struct CallDep {
3535

3636
#[nativebridge::native_bridge(CubeEvaluatorStatic)]
3737
pub trait CubeEvaluator {
38-
#[nbridge(field)]
39-
fn primary_keys(&self) -> Result<HashMap<String, String>, CubeError>;
4038
fn parse_path(&self, path_type: String, path: String) -> Result<Vec<String>, CubeError>;
4139
fn measure_by_path(&self, measure_path: String)
4240
-> Result<Rc<dyn MeasureDefinition>, CubeError>;
@@ -51,11 +49,6 @@ pub trait CubeEvaluator {
5149
fn is_dimension(&self, path: Vec<String>) -> Result<bool, CubeError>;
5250
fn is_segment(&self, path: Vec<String>) -> Result<bool, CubeError>;
5351
fn cube_exists(&self, name: String) -> Result<bool, CubeError>;
54-
fn resolve_symbols_call_deps(
55-
&self,
56-
cube_name: String,
57-
sql: Rc<dyn MemberSql>,
58-
) -> Result<Vec<CallDep>, CubeError>;
5952
fn resolve_granularity(
6053
&self,
6154
path: Vec<String>,

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/member_sql.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use super::{
33
security_context::{NativeSecurityContext, SecurityContext},
44
sql_utils::NativeSqlUtils,
55
};
6+
use crate::cube_bridge::sql_utils::SqlUtils;
7+
use crate::planner::sql_evaluator::SqlCallArg;
68
use crate::utils::UniqueVector;
7-
use crate::{cube_bridge::base_tools::BaseTools, planner::sql_evaluator::SqlCallArg};
89
use cubenativeutils::wrappers::object::{NativeFunction, NativeStruct, NativeType};
910
use cubenativeutils::wrappers::serializer::{NativeDeserialize, NativeSerialize};
1011
use cubenativeutils::wrappers::NativeContextHolderRef;
@@ -300,7 +301,7 @@ pub trait MemberSql {
300301
fn as_any(self: Rc<Self>) -> Rc<dyn Any>;
301302
fn compile_template_sql(
302303
&self,
303-
base_tools: Rc<dyn BaseTools>,
304+
sql_utils: Rc<dyn SqlUtils>,
304305
security_context: Rc<dyn SecurityContext>,
305306
) -> Result<(SqlTemplate, SqlTemplateArgs), CubeError>;
306307
}
@@ -633,7 +634,7 @@ impl<IT: InnerTypes> MemberSql for NativeMemberSql<IT> {
633634

634635
fn compile_template_sql(
635636
&self,
636-
base_tools: Rc<dyn BaseTools>,
637+
sql_utils: Rc<dyn SqlUtils>,
637638
security_context: Rc<dyn SecurityContext>,
638639
) -> Result<(SqlTemplate, SqlTemplateArgs), CubeError> {
639640
let state = ProxyState::new();
@@ -666,8 +667,8 @@ impl<IT: InnerTypes> MemberSql for NativeMemberSql<IT> {
666667
context_obj,
667668
)?
668669
} else if arg == "SQL_UTILS" {
669-
base_tools
670-
.sql_utils_for_rust()?
670+
sql_utils
671+
.clone()
671672
.as_any()
672673
.downcast::<NativeSqlUtils<IT>>()
673674
.unwrap()

rust/cubesqlplanner/cubesqlplanner/src/cube_bridge/string_or_sql.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@ use cubenativeutils::wrappers::inner_types::InnerTypes;
33
use cubenativeutils::wrappers::serializer::NativeDeserialize;
44
use cubenativeutils::wrappers::NativeObjectHandle;
55
use cubenativeutils::CubeError;
6+
use std::fmt;
67
use std::rc::Rc;
78

9+
#[derive(Clone)]
810
pub enum StringOrSql {
911
String(String),
1012
MemberSql(Rc<dyn StructWithSqlMember>),
1113
}
1214

15+
impl fmt::Debug for StringOrSql {
16+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17+
match self {
18+
StringOrSql::String(s) => write!(f, "String({:?})", s),
19+
StringOrSql::MemberSql(_) => write!(f, "MemberSql(<trait object>)"),
20+
}
21+
}
22+
}
23+
1324
impl<IT: InnerTypes> NativeDeserialize<IT> for StringOrSql {
1425
fn from_native(native_object: NativeObjectHandle<IT>) -> Result<Self, CubeError> {
1526
match String::from_native(native_object.clone()) {

rust/cubesqlplanner/cubesqlplanner/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ pub mod logical_plan;
33
pub mod physical_plan_builder;
44
pub mod plan;
55
pub mod planner;
6+
#[cfg(test)]
7+
pub(crate) mod test_fixtures;
8+
#[cfg(test)]
9+
mod tests;
610
pub(crate) mod utils;

rust/cubesqlplanner/cubesqlplanner/src/planner/query_tools.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl QueryTools {
135135
};
136136
let evaluator_compiler = Rc::new(RefCell::new(Compiler::new(
137137
cube_evaluator.clone(),
138-
base_tools.clone(),
138+
base_tools.sql_utils_for_rust()?,
139139
security_context.clone(),
140140
timezone.clone(),
141141
)));

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/compiler.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ use super::{
44
CubeNameSymbolFactory, CubeTableSymbolFactory, DimensionSymbolFactory, MeasureSymbolFactory,
55
SqlCall, SymbolFactory, TraversalVisitor,
66
};
7-
use crate::cube_bridge::base_tools::BaseTools;
87
use crate::cube_bridge::evaluator::CubeEvaluator;
98
use crate::cube_bridge::join_hints::JoinHintItem;
109
use crate::cube_bridge::member_sql::MemberSql;
1110
use crate::cube_bridge::security_context::SecurityContext;
11+
use crate::cube_bridge::sql_utils::SqlUtils;
1212
use crate::planner::sql_evaluator::sql_call_builder::SqlCallBuilder;
1313
use chrono_tz::Tz;
1414
use cubenativeutils::CubeError;
1515
use std::collections::HashMap;
1616
use std::rc::Rc;
1717
pub struct Compiler {
1818
cube_evaluator: Rc<dyn CubeEvaluator>,
19-
base_tools: Rc<dyn BaseTools>,
19+
sql_utils: Rc<dyn SqlUtils>,
2020
security_context: Rc<dyn SecurityContext>,
2121
timezone: Tz,
2222
/* (type, name) */
@@ -26,14 +26,14 @@ pub struct Compiler {
2626
impl Compiler {
2727
pub fn new(
2828
cube_evaluator: Rc<dyn CubeEvaluator>,
29-
base_tools: Rc<dyn BaseTools>,
29+
sql_utils: Rc<dyn SqlUtils>,
3030
security_context: Rc<dyn SecurityContext>,
3131
timezone: Tz,
3232
) -> Self {
3333
Self {
3434
cube_evaluator,
3535
security_context,
36-
base_tools,
36+
sql_utils,
3737
timezone,
3838
members: HashMap::new(),
3939
}
@@ -56,10 +56,6 @@ impl Compiler {
5656
}
5757
}
5858

59-
pub fn base_tools(&self) -> Rc<dyn BaseTools> {
60-
self.base_tools.clone()
61-
}
62-
6359
pub fn add_measure_evaluator(
6460
&mut self,
6561
measure: String,
@@ -136,6 +132,7 @@ impl Compiler {
136132
let call_builder = SqlCallBuilder::new(
137133
self,
138134
self.cube_evaluator.clone(),
135+
self.sql_utils.clone(),
139136
self.security_context.clone(),
140137
);
141138
let sql_call = call_builder.build(&cube_name, member_sql.clone())?;

rust/cubesqlplanner/cubesqlplanner/src/planner/sql_evaluator/sql_call_builder.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::{SqlCall, SqlCallDependency, SqlCallFilterGroupItem, SqlCallFilterPar
44
use crate::cube_bridge::evaluator::CubeEvaluator;
55
use crate::cube_bridge::member_sql::*;
66
use crate::cube_bridge::security_context::SecurityContext;
7+
use crate::cube_bridge::sql_utils::SqlUtils;
78
use crate::planner::sql_evaluator::TimeDimensionSymbol;
89
use crate::planner::GranularityHelper;
910
use cubenativeutils::CubeError;
@@ -12,18 +13,21 @@ use std::rc::Rc;
1213
pub struct SqlCallBuilder<'a> {
1314
compiler: &'a mut Compiler,
1415
cube_evaluator: Rc<dyn CubeEvaluator>,
16+
sql_utils: Rc<dyn SqlUtils>,
1517
security_context: Rc<dyn SecurityContext>,
1618
}
1719

1820
impl<'a> SqlCallBuilder<'a> {
1921
pub fn new(
2022
compiler: &'a mut Compiler,
2123
cube_evaluator: Rc<dyn CubeEvaluator>,
24+
sql_utils: Rc<dyn SqlUtils>,
2225
security_context: Rc<dyn SecurityContext>,
2326
) -> Self {
2427
Self {
2528
compiler,
2629
cube_evaluator,
30+
sql_utils,
2731
security_context,
2832
}
2933
}
@@ -34,7 +38,7 @@ impl<'a> SqlCallBuilder<'a> {
3438
member_sql: Rc<dyn MemberSql>,
3539
) -> Result<SqlCall, CubeError> {
3640
let (template, template_args) = member_sql
37-
.compile_template_sql(self.compiler.base_tools(), self.security_context.clone())?;
41+
.compile_template_sql(self.sql_utils.clone(), self.security_context.clone())?;
3842

3943
let deps = template_args
4044
.symbol_paths
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/// Macro to implement static_data() helper method for mock bridge types
2+
///
3+
/// This macro generates a helper method that returns an owned StaticData struct.
4+
/// The helper is used by the trait's static_data() method which applies Box::leak.
5+
///
6+
/// # Usage
7+
/// ```ignore
8+
/// impl_static_data!(
9+
/// MockDimensionDefinition, // The mock type
10+
/// DimensionDefinitionStatic, // The static data type
11+
/// dimension_type, // Fields to include
12+
/// owned_by_cube,
13+
/// multi_stage
14+
/// );
15+
/// ```
16+
///
17+
/// # Generated Code
18+
/// ```ignore
19+
/// impl MockDimensionDefinition {
20+
/// pub fn static_data(&self) -> DimensionDefinitionStatic {
21+
/// DimensionDefinitionStatic {
22+
/// dimension_type: self.dimension_type.clone(),
23+
/// owned_by_cube: self.owned_by_cube.clone(),
24+
/// multi_stage: self.multi_stage.clone(),
25+
/// }
26+
/// }
27+
/// }
28+
/// ```
29+
#[macro_export]
30+
macro_rules! impl_static_data {
31+
// Pattern: impl_static_data!(MockType, StaticType, field1, field2, ...)
32+
($mock_type:ty, $static_type:path, $($field:ident),* $(,)?) => {
33+
// Helper method that returns owned StaticData
34+
impl $mock_type {
35+
pub fn static_data(&self) -> $static_type {
36+
$static_type {
37+
$($field: self.$field.clone()),*
38+
}
39+
}
40+
}
41+
};
42+
}
43+
44+
/// Macro to implement the trait's static_data() method using Box::leak
45+
///
46+
/// This macro should be used INSIDE the trait implementation block to generate
47+
/// the static_data() method that returns &'static references.
48+
///
49+
/// # Memory Leak Explanation
50+
/// This macro uses `Box::leak(Box::new(...))` to convert owned values into static
51+
/// references. This intentionally leaks memory, which is acceptable because:
52+
/// - Mock objects are only used in tests with short lifetimes
53+
/// - Tests typically create a small number of mock objects
54+
/// - The leaked memory is minimal and reclaimed when the test process exits
55+
/// - This approach significantly simplifies test code by avoiding complex lifetime management
56+
///
57+
/// # Usage
58+
/// ```ignore
59+
/// impl DimensionDefinition for MockDimensionDefinition {
60+
/// impl_static_data_method!(DimensionDefinitionStatic);
61+
///
62+
/// fn sql(&self) -> Result<Option<Rc<dyn MemberSql>>, CubeError> {
63+
/// // ... other trait methods
64+
/// }
65+
/// }
66+
/// ```
67+
///
68+
/// # Generated Code
69+
/// ```ignore
70+
/// fn static_data(&self) -> &DimensionDefinitionStatic {
71+
/// // Intentional memory leak - acceptable for test mocks
72+
/// // The Box::leak pattern converts the owned value to a static reference
73+
/// Box::leak(Box::new(Self::static_data(self)))
74+
/// }
75+
/// ```
76+
#[macro_export]
77+
macro_rules! impl_static_data_method {
78+
($static_type:path) => {
79+
fn static_data(&self) -> &$static_type {
80+
// Intentional memory leak for test mocks - see macro documentation
81+
// This converts the owned StaticData from the helper method into a &'static reference
82+
// required by the trait. The leak is acceptable because:
83+
// 1. Test mocks have short lifetimes (duration of test)
84+
// 2. Small number of instances created
85+
// 3. Memory reclaimed when test process exits
86+
Box::leak(Box::new(Self::static_data(self)))
87+
}
88+
};
89+
}
90+
91+
#[cfg(test)]
92+
mod tests {
93+
use serde::{Deserialize, Serialize};
94+
use typed_builder::TypedBuilder;
95+
96+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
97+
pub struct TestStatic {
98+
pub name: String,
99+
pub value: Option<i32>,
100+
}
101+
102+
pub trait TestTrait {
103+
fn static_data(&self) -> &TestStatic;
104+
}
105+
106+
#[derive(TypedBuilder)]
107+
pub struct MockTest {
108+
#[builder(default = "test".to_string())]
109+
name: String,
110+
#[builder(default)]
111+
value: Option<i32>,
112+
}
113+
114+
impl_static_data!(MockTest, TestStatic, name, value);
115+
116+
impl TestTrait for MockTest {
117+
impl_static_data_method!(TestStatic);
118+
}
119+
120+
#[test]
121+
fn test_static_data_helper_method() {
122+
let mock = MockTest::builder()
123+
.name("hello".to_string())
124+
.value(Some(42))
125+
.build();
126+
127+
let static_data = mock.static_data();
128+
assert_eq!(static_data.name, "hello");
129+
assert_eq!(static_data.value, Some(42));
130+
}
131+
132+
#[test]
133+
fn test_static_data_trait_method() {
134+
let mock = MockTest::builder()
135+
.name("world".to_string())
136+
.value(Some(123))
137+
.build();
138+
139+
// Call trait method
140+
let static_data: &TestStatic = TestTrait::static_data(&mock);
141+
assert_eq!(static_data.name, "world");
142+
assert_eq!(static_data.value, Some(123));
143+
}
144+
145+
#[test]
146+
fn test_static_data_macro_with_defaults() {
147+
let mock = MockTest::builder().build();
148+
149+
let static_data = mock.static_data();
150+
assert_eq!(static_data.name, "test");
151+
assert_eq!(static_data.value, None);
152+
}
153+
}

0 commit comments

Comments
 (0)