#![allow(clippy::result_large_err)]
use std::collections::HashMap;
use qlexpress::aparser::import_manager::QLImport;
use qlexpress::default_class_supplier::DefaultClassSupplier;
use qlexpress::init_options::InitOptions;
use qlexpress::ql_options::QLOptions;
use qlexpress::runtime::value::DataValue;
use qlexpress::security::ql_security_strategy::QLSecurityStrategy;
use qlexpress::Express4Runner;
fn runner() -> Express4Runner {
use qlexpress::runtime::native_type::NativeType;
use std::rc::Rc;
let mut supplier = DefaultClassSupplier::instance();
for cls in [
"java.lang.Exception",
"java.lang.RuntimeException",
"java.lang.Throwable",
] {
supplier.register(cls);
}
let imports = [
QLImport::import_cls("java.lang.Exception"),
QLImport::import_cls("java.lang.RuntimeException"),
];
let mut runner = Express4Runner::with_init_options(
InitOptions::builder()
.class_supplier(Rc::new(supplier))
.add_default_import(imports.to_vec())
.security_strategy(QLSecurityStrategy::open())
.build(),
);
let mut runtime_exc = NativeType::named("java.lang.RuntimeException");
runtime_exc.constructor = Some(Rc::new(|args| {
let msg = args
.first()
.map(|v| v.string_value_of())
.unwrap_or_default();
Ok(DataValue::string(format!("RuntimeException({msg})")))
}));
runner.register_native_type(runtime_exc);
let mut exc = NativeType::named("java.lang.Exception");
exc.constructor = Some(Rc::new(|args| {
let msg = args
.first()
.map(|v| v.string_value_of())
.unwrap_or_default();
Ok(DataValue::string(format!("Exception({msg})")))
}));
runner.register_native_type(exc);
runner
}
fn opts() -> QLOptions {
QLOptions::builder().timeout_millis(2000).build()
}
fn run(
script: &str,
) -> Result<qlexpress::runtime::value::DataValue, qlexpress::exception::QLException> {
let r = runner().execute(script, HashMap::new(), &opts())?;
Ok(r.into_result())
}
fn run_int(script: &str) -> i64 {
match run(script).expect("script ok") {
qlexpress::runtime::value::DataValue::Long(n) => n,
qlexpress::runtime::value::DataValue::Int(n) => n as i64,
other => panic!("expected int/long, got {other:?}"),
}
}
#[test]
fn break_inside_try_should_exit_loop() {
let script = "\
result = 0;\n\
for (int i = 0; i < 10; i = i + 1) {\n\
try {\n\
if (i == 3) {\n\
break;\n\
}\n\
result = result + 1;\n\
} catch (Exception e) {}\n\
}\n\
result";
assert_eq!(run_int(script), 3);
}
#[test]
fn continue_inside_try_should_skip_iteration() {
let script = "\
result = 0;\n\
for (int i = 0; i < 5; i = i + 1) {\n\
try {\n\
if (i == 2 || i == 4) {\n\
continue;\n\
}\n\
result = result + i;\n\
} catch (Exception e) {}\n\
}\n\
result";
assert_eq!(run_int(script), 4);
}
#[test]
fn break_inside_try_with_finally_should_exit_loop() {
let script = "\
result = 0;\n\
for (int i = 0; i < 10; i = i + 1) {\n\
try {\n\
if (i == 2) {\n\
break;\n\
}\n\
result = result + 1;\n\
} catch (Exception e) {\n\
} finally {\n\
}\n\
}\n\
result";
assert_eq!(run_int(script), 2);
}
#[test]
fn continue_inside_try_with_finally_should_skip_iteration() {
let script = "\
result = 0;\n\
for (int i = 0; i < 5; i = i + 1) {\n\
try {\n\
if (i == 3) {\n\
continue;\n\
}\n\
result = result + i;\n\
} catch (Exception e) {\n\
} finally {\n\
}\n\
}\n\
result";
assert_eq!(run_int(script), 7);
}
#[test]
fn break_inside_catch_should_exit_loop() {
let script = "\
result = 0;\n\
for (int i = 0; i < 10; i = i + 1) {\n\
try {\n\
if (i == 3) {\n\
throw 1;\n\
}\n\
result = result + 1;\n\
} catch (e) {\n\
break;\n\
}\n\
}\n\
result";
assert_eq!(run_int(script), 3);
}
#[test]
fn continue_inside_catch_should_skip_iteration() {
let script = "\
result = 0;\n\
for (int i = 0; i < 5; i = i + 1) {\n\
try {\n\
if (i == 2) {\n\
throw 1;\n\
}\n\
result = result + i;\n\
} catch (e) {\n\
continue;\n\
}\n\
}\n\
result";
assert_eq!(run_int(script), 8);
}
#[test]
fn normal_try_expression_inside_while_should_not_skip_following_statement() {
let script = "\
i = 0;\n\
result = 0;\n\
while (i < 3) {\n\
try {\n\
result = result + 1;\n\
} catch (Exception e) {}\n\
i = i + 1;\n\
}\n\
result";
assert_eq!(run_int(script), 3);
}
#[test]
fn break_inside_while_try_should_exit_loop() {
let script = "\
i = 0;\n\
result = 0;\n\
while (i < 10) {\n\
try {\n\
if (i == 5) {\n\
break;\n\
}\n\
result = result + 1;\n\
} catch (Exception e) {}\n\
i = i + 1;\n\
}\n\
result";
assert_eq!(run_int(script), 5);
}
#[test]
fn continue_inside_while_try_should_skip_rest_of_body() {
let script = "\
i = 0;\n\
result = 0;\n\
tail = 0;\n\
while (i < 5) {\n\
i = i + 1;\n\
try {\n\
if (i == 3) {\n\
continue;\n\
}\n\
result = result + i;\n\
} catch (Exception e) {}\n\
tail = tail + 10;\n\
}\n\
result * 100 + tail";
assert_eq!(run_int(script), 1240);
}
#[test]
fn break_inside_nested_try_should_exit_loop() {
let script = "\
result = 0;\n\
for (int i = 0; i < 10; i = i + 1) {\n\
try {\n\
try {\n\
if (i == 4) {\n\
break;\n\
}\n\
result = result + 1;\n\
} catch (Exception e) {}\n\
} catch (Exception e) {}\n\
}\n\
result";
assert_eq!(run_int(script), 4);
}