adjust all tests to nix_store_open's new signature
(cherry picked from commit da9bf071709bc9e48a0a02531e78b2a71a9b4eba)
This commit is contained in:
parent
de09abb084
commit
ec28176852
2 changed files with 36 additions and 32 deletions
|
|
@ -403,6 +403,8 @@ pub fn test_init() {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ctor::ctor;
|
use ctor::ctor;
|
||||||
use std::io::Write as _;
|
use std::io::Write as _;
|
||||||
|
|
||||||
|
|
@ -417,7 +419,7 @@ mod tests {
|
||||||
fn eval_state_new_and_drop() {
|
fn eval_state_new_and_drop() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
// very basic test: make sure initialization doesn't crash
|
// very basic test: make sure initialization doesn't crash
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let _e = EvalState::new(store, []).unwrap();
|
let _e = EvalState::new(store, []).unwrap();
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -433,11 +435,11 @@ mod tests {
|
||||||
writeln!(test_file0, "{integer0}").unwrap();
|
writeln!(test_file0, "{integer0}").unwrap();
|
||||||
writeln!(test_file1, "{integer1}").unwrap();
|
writeln!(test_file1, "{integer1}").unwrap();
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let es = EvalState::new(Store::open("auto").unwrap(), []).unwrap();
|
let es = EvalState::new(Store::open("auto", HashMap::new()).unwrap(), []).unwrap();
|
||||||
assert!(es.eval_from_string(import_expression, "<test>").is_err());
|
assert!(es.eval_from_string(import_expression, "<test>").is_err());
|
||||||
|
|
||||||
let es = EvalState::new(
|
let es = EvalState::new(
|
||||||
Store::open("auto").unwrap(),
|
Store::open("auto", HashMap::new()).unwrap(),
|
||||||
[
|
[
|
||||||
format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(),
|
format!("test_file0={}", test_file0.path().to_str().unwrap()).as_str(),
|
||||||
format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(),
|
format!("test_file1={}", test_file1.path().to_str().unwrap()).as_str(),
|
||||||
|
|
@ -457,7 +459,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_eval_from_string() {
|
fn eval_state_eval_from_string() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("1", "<test>").unwrap();
|
let v = es.eval_from_string("1", "<test>").unwrap();
|
||||||
let v2 = v.clone();
|
let v2 = v.clone();
|
||||||
|
|
@ -474,7 +476,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_bool() {
|
fn eval_state_value_bool() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("true", "<test>").unwrap();
|
let v = es.eval_from_string("true", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -487,7 +489,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_int() {
|
fn eval_state_value_int() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("1", "<test>").unwrap();
|
let v = es.eval_from_string("1", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -502,7 +504,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_attrs_names_empty() {
|
fn eval_state_value_attrs_names_empty() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("{ }", "<test>").unwrap();
|
let v = es.eval_from_string("{ }", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -517,7 +519,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_require_attrs_names_bad_type() {
|
fn eval_state_require_attrs_names_bad_type() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("1", "<test>").unwrap();
|
let v = es.eval_from_string("1", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -534,7 +536,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_attrs_names_example() {
|
fn eval_state_value_attrs_names_example() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#;
|
let expr = r#"{ a = throw "nope a"; b = throw "nope b"; }"#;
|
||||||
let v = es.eval_from_string(expr, "<test>").unwrap();
|
let v = es.eval_from_string(expr, "<test>").unwrap();
|
||||||
|
|
@ -549,7 +551,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_require_attrs_select() {
|
fn eval_state_require_attrs_select() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let expr = r#"{ a = "aye"; b = "bee"; }"#;
|
let expr = r#"{ a = "aye"; b = "bee"; }"#;
|
||||||
let v = es.eval_from_string(expr, "<test>").unwrap();
|
let v = es.eval_from_string(expr, "<test>").unwrap();
|
||||||
|
|
@ -564,7 +566,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_require_attrs_select_error() {
|
fn eval_state_require_attrs_select_error() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let expr = r#"{ a = throw "oh no the error"; }"#;
|
let expr = r#"{ a = throw "oh no the error"; }"#;
|
||||||
let v = es.eval_from_string(expr, "<test>").unwrap();
|
let v = es.eval_from_string(expr, "<test>").unwrap();
|
||||||
|
|
@ -585,7 +587,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_require_attrs_select_opt() {
|
fn eval_state_require_attrs_select_opt() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let expr = r#"{ a = "aye"; b = "bee"; }"#;
|
let expr = r#"{ a = "aye"; b = "bee"; }"#;
|
||||||
let v = es.eval_from_string(expr, "<test>").unwrap();
|
let v = es.eval_from_string(expr, "<test>").unwrap();
|
||||||
|
|
@ -602,7 +604,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_require_attrs_select_opt_error() {
|
fn eval_state_require_attrs_select_opt_error() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let expr = r#"{ a = throw "oh no the error"; }"#;
|
let expr = r#"{ a = throw "oh no the error"; }"#;
|
||||||
let v = es.eval_from_string(expr, "<test>").unwrap();
|
let v = es.eval_from_string(expr, "<test>").unwrap();
|
||||||
|
|
@ -623,7 +625,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_string() {
|
fn eval_state_value_string() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("\"hello\"", "<test>").unwrap();
|
let v = es.eval_from_string("\"hello\"", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -638,7 +640,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_string_unexpected_bool() {
|
fn eval_state_value_string_unexpected_bool() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("true", "<test>").unwrap();
|
let v = es.eval_from_string("true", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -656,7 +658,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_string_unexpected_path_value() {
|
fn eval_state_value_string_unexpected_path_value() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("/foo", "<test>").unwrap();
|
let v = es.eval_from_string("/foo", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -673,7 +675,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_string_bad_utf() {
|
fn eval_state_value_string_bad_utf() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es
|
let v = es
|
||||||
.eval_from_string("builtins.substring 0 1 \"ü\"", "<test>")
|
.eval_from_string("builtins.substring 0 1 \"ü\"", "<test>")
|
||||||
|
|
@ -694,7 +696,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_string_unexpected_context() {
|
fn eval_state_value_string_unexpected_context() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es
|
let v = es
|
||||||
.eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "<test>")
|
.eval_from_string("(derivation { name = \"hello\"; system = \"dummy\"; builder = \"cmd.exe\"; }).outPath", "<test>")
|
||||||
|
|
@ -713,7 +715,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_new_string() {
|
fn eval_state_new_string() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.new_value_str("hello").unwrap();
|
let v = es.new_value_str("hello").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -728,7 +730,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_new_string_empty() {
|
fn eval_state_new_string_empty() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.new_value_str("").unwrap();
|
let v = es.new_value_str("").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -743,7 +745,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_new_string_invalid() {
|
fn eval_state_new_string_invalid() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let r = es.new_value_str("hell\0no");
|
let r = es.new_value_str("hell\0no");
|
||||||
match r {
|
match r {
|
||||||
|
|
@ -762,7 +764,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_new_int() {
|
fn eval_state_new_int() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.new_value_int(42).unwrap();
|
let v = es.new_value_int(42).unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -777,7 +779,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_attrset() {
|
fn eval_state_value_attrset() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es.eval_from_string("{ }", "<test>").unwrap();
|
let v = es.eval_from_string("{ }", "<test>").unwrap();
|
||||||
es.force(&v).unwrap();
|
es.force(&v).unwrap();
|
||||||
|
|
@ -790,7 +792,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_value_list() {
|
fn eval_state_value_list() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let v = es
|
let v = es
|
||||||
.eval_from_string("[ ]", "<test>")
|
.eval_from_string("[ ]", "<test>")
|
||||||
|
|
@ -805,7 +807,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_realise_string() {
|
fn eval_state_realise_string() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let expr = r#"
|
let expr = r#"
|
||||||
''
|
''
|
||||||
|
|
@ -852,7 +854,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_call() {
|
fn eval_state_call() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
|
let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
|
||||||
let a = es.eval_from_string("2", "<test>").unwrap();
|
let a = es.eval_from_string("2", "<test>").unwrap();
|
||||||
|
|
@ -888,7 +890,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_call_fail_body() {
|
fn eval_state_call_fail_body() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
|
let f = es.eval_from_string("x: x + 1", "<test>").unwrap();
|
||||||
let a = es.eval_from_string("true", "<test>").unwrap();
|
let a = es.eval_from_string("true", "<test>").unwrap();
|
||||||
|
|
@ -933,7 +935,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_state_call_fail_args() {
|
fn eval_state_call_fail_args() {
|
||||||
gc_registering_current_thread(|| {
|
gc_registering_current_thread(|| {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let es = EvalState::new(store, []).unwrap();
|
let es = EvalState::new(store, []).unwrap();
|
||||||
let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
|
let f = es.eval_from_string("{x}: x + 1", "<test>").unwrap();
|
||||||
let a = es.eval_from_string("{}", "<test>").unwrap();
|
let a = es.eval_from_string("{}", "<test>").unwrap();
|
||||||
|
|
|
||||||
|
|
@ -112,23 +112,25 @@ impl Store {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_works() {
|
fn auto_works() {
|
||||||
let res = Store::open("auto");
|
let res = Store::open("auto", HashMap::new());
|
||||||
res.unwrap();
|
res.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_uri_fails() {
|
fn invalid_uri_fails() {
|
||||||
let res = Store::open("invalid://uri");
|
let res = Store::open("invalid://uri", HashMap::new());
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_uri() {
|
fn get_uri() {
|
||||||
let store = Store::open("auto").unwrap();
|
let store = Store::open("auto", HashMap::new()).unwrap();
|
||||||
let uri = store.get_uri().unwrap();
|
let uri = store.get_uri().unwrap();
|
||||||
assert!(!uri.is_empty());
|
assert!(!uri.is_empty());
|
||||||
// must be ascii
|
// must be ascii
|
||||||
|
|
@ -140,7 +142,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] // Needs network access
|
#[ignore] // Needs network access
|
||||||
fn get_uri_nixos_cache() {
|
fn get_uri_nixos_cache() {
|
||||||
let store = Store::open("https://cache.nixos.org/").unwrap();
|
let store = Store::open("https://cache.nixos.org/", HashMap::new()).unwrap();
|
||||||
let uri = store.get_uri().unwrap();
|
let uri = store.get_uri().unwrap();
|
||||||
assert_eq!(uri, "https://cache.nixos.org");
|
assert_eq!(uri, "https://cache.nixos.org");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue