Extend test coverage for test and fixed modules
Some checks are pending
/ verify (push) Waiting to run
Some checks are pending
/ verify (push) Waiting to run
This commit is contained in:
parent
9024e56e72
commit
dc4c331cb8
6 changed files with 226 additions and 46 deletions
|
|
@ -82,7 +82,8 @@ test-cover-quick:
|
||||||
# Quickly update coverage reports (inaccurate)
|
# Quickly update coverage reports (inaccurate)
|
||||||
[group: 'assess']
|
[group: 'assess']
|
||||||
test-cover-watch-quick:
|
test-cover-watch-quick:
|
||||||
{{ watch_cmd }} {{ just_cmd }} test-cover-quick
|
@{{ watch_cmd }} {{ just_cmd_no_ts }} test-cover-quick 2>&1 \
|
||||||
|
| grep -v "process didn't exit successfully:" || true
|
||||||
|
|
||||||
alias oq := test-cover-watch-quick
|
alias oq := test-cover-watch-quick
|
||||||
|
|
||||||
|
|
@ -441,9 +442,10 @@ glibc_target := "x86_64-unknown-linux-gnu"
|
||||||
default_target := musl_target
|
default_target := musl_target
|
||||||
|
|
||||||
debug_vars := 'DEBUG=${DEBUG:-} DEBUG_FILTER=${DEBUG_FILTER:-} RUST_BACKTRACE=${RUST_BACKTRACE:-} RUSTFLAGS=${RUSTFLAGS:-}'
|
debug_vars := 'DEBUG=${DEBUG:-} DEBUG_FILTER=${DEBUG_FILTER:-} RUST_BACKTRACE=${RUST_BACKTRACE:-} RUSTFLAGS=${RUSTFLAGS:-}'
|
||||||
|
just_cmd := 'just --timestamp --explain --command-color green'
|
||||||
|
just_cmd_no_ts := 'just --explain --command-color green'
|
||||||
watch_cmd := "watchexec -qc -r -e rs,toml,html --color always -- "
|
watch_cmd := "watchexec -qc -r -e rs,toml,html --color always -- "
|
||||||
cover_cmd := 'cargo llvm-cov --color always --ignore-filename-regex "main\.rs|log\.rs"'
|
cover_cmd := 'cargo llvm-cov --color always --ignore-filename-regex "main\.rs|log\.rs"'
|
||||||
just_cmd := 'just --timestamp --explain --command-color green'
|
|
||||||
|
|
||||||
last_tag := `git tag --sort=-creatordate | head -1 | tr -d v`
|
last_tag := `git tag --sort=-creatordate | head -1 | tr -d v`
|
||||||
manifest_version := `grep "^version" Cargo.toml | cut -d \" -f 2`
|
manifest_version := `grep "^version" Cargo.toml | cut -d \" -f 2`
|
||||||
|
|
|
||||||
32
Cargo.lock
generated
32
Cargo.lock
generated
|
|
@ -528,9 +528,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.17"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
|
|
@ -1102,7 +1102,7 @@ dependencies = [
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"toml_writer",
|
"toml_writer",
|
||||||
"winnow",
|
"winnow 0.7.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1116,18 +1116,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_parser"
|
||||||
version = "1.0.9+spec-1.1.0"
|
version = "1.0.10+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow",
|
"winnow 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_writer"
|
name = "toml_writer"
|
||||||
version = "1.0.6+spec-1.1.0"
|
version = "1.0.7+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
|
|
@ -1475,19 +1475,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "winnow"
|
||||||
version = "0.8.42"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
|
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.47"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.42"
|
version = "0.8.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
|
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
||||||
|
|
@ -268,5 +268,4 @@ zero_sized_map_values = "warn"
|
||||||
# cargo
|
# cargo
|
||||||
negative_feature_names = "warn"
|
negative_feature_names = "warn"
|
||||||
redundant_feature_names = "warn"
|
redundant_feature_names = "warn"
|
||||||
multiple_crate_versions = "warn"
|
|
||||||
wildcard_dependencies = "warn"
|
wildcard_dependencies = "warn"
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ use std::{env, fs, io, path::PathBuf};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Directories {
|
pub struct Directories {
|
||||||
pub original: PathBuf,
|
pub original: PathBuf,
|
||||||
pub templates: PathBuf,
|
pub templates: PathBuf,
|
||||||
|
pub assets: PathBuf,
|
||||||
pub test: PathBuf,
|
pub test: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,6 +22,7 @@ impl Directories {
|
||||||
let original = env::current_dir()?;
|
let original = env::current_dir()?;
|
||||||
let test = original.join(format!("target/mocks/{dir_name}"));
|
let test = original.join(format!("target/mocks/{dir_name}"));
|
||||||
let templates = test.join("templates");
|
let templates = test.join("templates");
|
||||||
|
let assets = test.join("static").join("public").join("assets");
|
||||||
|
|
||||||
drop(fs::remove_dir_all(&test));
|
drop(fs::remove_dir_all(&test));
|
||||||
|
|
||||||
|
|
@ -37,6 +40,13 @@ impl Directories {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err(error) = fs::create_dir_all(&assets) {
|
||||||
|
return Err(Error::with_io(
|
||||||
|
"Failed 'assets' directory creation",
|
||||||
|
error,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(error) = env::set_current_dir(&test) {
|
if let Err(error) = env::set_current_dir(&test) {
|
||||||
return Err(Error::with_io("Failed current directory change", error))
|
return Err(Error::with_io("Failed current directory change", error))
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +54,7 @@ impl Directories {
|
||||||
Ok(Directories {
|
Ok(Directories {
|
||||||
original,
|
original,
|
||||||
templates,
|
templates,
|
||||||
|
assets,
|
||||||
test,
|
test,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -68,10 +79,10 @@ pub struct Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
fn with_io(message: &str, inner_io: io::Error) -> Error {
|
fn with_io(message: &str, inner_error: io::Error) -> Error {
|
||||||
Error {
|
Error {
|
||||||
message: String::from(message),
|
message: String::from(message),
|
||||||
inner_io: Some(inner_io),
|
inner_io: Some(inner_error),
|
||||||
inner_tera: None,
|
inner_tera: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,4 +143,48 @@ mod tests {
|
||||||
let dirs = Directories::setup("\0");
|
let dirs = Directories::setup("\0");
|
||||||
assert!(dirs.is_err());
|
assert!(dirs.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_contains_str_from_from() {
|
||||||
|
let payload = "rHneusPkYNGW0Ia0";
|
||||||
|
let error = Error::from(payload);
|
||||||
|
assert!(format!("{error}").contains(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_contains_str_from_io_error() {
|
||||||
|
let payload = "SsVi0d3Ywc8kVhwp";
|
||||||
|
let io_payload = "LoPbZP7cJEHzAjGW";
|
||||||
|
let io_error = std::io::Error::other(io_payload);
|
||||||
|
let error = Error::with_io(payload, io_error);
|
||||||
|
assert!(format!("{error}").contains(payload));
|
||||||
|
assert!(format!("{error}").contains(io_payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_io_error() {
|
||||||
|
let payload = "YgmTKBm3VtHt5h3x9";
|
||||||
|
let io_error = std::io::Error::other(payload);
|
||||||
|
let error = Error::from(io_error);
|
||||||
|
|
||||||
|
assert!(error.message.contains(payload));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod serial_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn failed_working_directory_reset() {
|
||||||
|
let dirs = Directories::setup("\0");
|
||||||
|
|
||||||
|
let error = dirs.unwrap_err();
|
||||||
|
println!("{error}");
|
||||||
|
assert!(error.message.contains("Failed test's directory creation"));
|
||||||
|
assert!(
|
||||||
|
format!("{error}")
|
||||||
|
.contains("file name contained an unexpected NUL byte")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,9 @@ fn assemble(asset: Asset, graph: &Graph) -> Response<Body> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::upper_case_acronyms)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum AssetErrorKind {
|
#[expect(clippy::upper_case_acronyms)]
|
||||||
|
pub enum AssetErrorKind {
|
||||||
NotFound,
|
NotFound,
|
||||||
IO,
|
IO,
|
||||||
UTF8,
|
UTF8,
|
||||||
|
|
@ -96,11 +96,11 @@ enum AssetErrorKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct AssetError {
|
pub struct AssetError {
|
||||||
path: String,
|
pub path: String,
|
||||||
kind: AssetErrorKind,
|
pub kind: AssetErrorKind,
|
||||||
io_error: Option<std::io::Error>,
|
pub io_error: Option<std::io::Error>,
|
||||||
utf8_error: Option<FromUtf8Error>,
|
pub utf8_error: Option<FromUtf8Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssetError {
|
impl AssetError {
|
||||||
|
|
@ -136,8 +136,7 @@ impl std::fmt::Display for AssetError {
|
||||||
let mut message = match self.kind {
|
let mut message = match self.kind {
|
||||||
AssetErrorKind::IO => {
|
AssetErrorKind::IO => {
|
||||||
format!(
|
format!(
|
||||||
"A default fallback for {} was found, \
|
"File {} was found, but it could not be loaded",
|
||||||
but it could not be loaded",
|
|
||||||
self.path
|
self.path
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -170,6 +169,7 @@ impl std::fmt::Display for AssetError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct Asset {
|
struct Asset {
|
||||||
blob: Option<Vec<u8>>,
|
blob: Option<Vec<u8>>,
|
||||||
text: Option<String>,
|
text: Option<String>,
|
||||||
|
|
@ -224,13 +224,14 @@ fn fallback(path: &str, graph: &Graph) -> Result<Asset, AssetError> {
|
||||||
let mime = mime::Mime::guess(path);
|
let mime = mime::Mime::guess(path);
|
||||||
|
|
||||||
match std::fs::read(&target) {
|
match std::fs::read(&target) {
|
||||||
// A matching file exists on disk
|
// A matching file exists on disk and is accessible
|
||||||
Ok(content) => Ok(Asset {
|
Ok(content) => Ok(Asset {
|
||||||
blob: Some(content),
|
blob: Some(content),
|
||||||
text: None,
|
text: None,
|
||||||
mime,
|
mime,
|
||||||
}),
|
}),
|
||||||
Err(io_error) => {
|
Err(io_error) => {
|
||||||
|
// A matching file does not exist on disk
|
||||||
if io_error.kind() == ErrorKind::NotFound {
|
if io_error.kind() == ErrorKind::NotFound {
|
||||||
if let Some(content) = defaults.get(path) {
|
if let Some(content) = defaults.get(path) {
|
||||||
Ok(Asset::from_str(content, mime))
|
Ok(Asset::from_str(content, mime))
|
||||||
|
|
@ -252,6 +253,7 @@ fn fallback(path: &str, graph: &Graph) -> Result<Asset, AssetError> {
|
||||||
None => not_found_error,
|
None => not_found_error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// A matching file exists on disk and is not accessible
|
||||||
} else {
|
} else {
|
||||||
Err(AssetError::new(
|
Err(AssetError::new(
|
||||||
path,
|
path,
|
||||||
|
|
@ -668,4 +670,99 @@ mod tests {
|
||||||
let response = file(Path("/k/j/m".to_string()), State(state)).await;
|
let response = file(Path("/k/j/m".to_string()), State(state)).await;
|
||||||
assert!(response.status() == StatusCode::NOT_FOUND);
|
assert!(response.status() == StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_from_utf8error() {
|
||||||
|
let bytes = vec![0, 159];
|
||||||
|
let utf8error = String::from_utf8(bytes.clone()).unwrap_err();
|
||||||
|
let error = AssetError::from(utf8error);
|
||||||
|
assert!(error.utf8_error.is_some());
|
||||||
|
assert_eq!(error.utf8_error.unwrap().into_bytes(), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_from_string() {
|
||||||
|
let payload = "r5MDnkEojW9HZDAG";
|
||||||
|
let asset_error = AssetError::from(payload.to_string());
|
||||||
|
println!("{asset_error}");
|
||||||
|
assert!(asset_error.path.contains(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_text_asset() {
|
||||||
|
let asset = Asset::new(&[1, 0, 1], mime::Mime::Txt).unwrap();
|
||||||
|
|
||||||
|
assert!(asset.blob.is_none());
|
||||||
|
assert!(asset.text.is_some());
|
||||||
|
assert_eq!(asset.text.unwrap(), "\u{1}\0\u{1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_blob_asset() {
|
||||||
|
let asset = Asset::new(&[1, 0, 1], mime::Mime::Png).unwrap();
|
||||||
|
|
||||||
|
assert!(asset.blob.is_some());
|
||||||
|
assert!(asset.text.is_none());
|
||||||
|
assert_eq!(asset.blob.unwrap(), &[1, 0, 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn asset_from_str() {
|
||||||
|
let payload = "\u{1}\0\u{6}";
|
||||||
|
let asset = Asset::from_str(payload, mime::Mime::Ico);
|
||||||
|
assert_eq!(asset.blob.unwrap(), &[1, 0, 6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_asset_utf8_error() {
|
||||||
|
let bad_bytes = [0xff, 0xc0, 0xf5, 0xc1, 0x80];
|
||||||
|
|
||||||
|
let error = Asset::new(&bad_bytes, mime::Mime::Txt).unwrap_err();
|
||||||
|
|
||||||
|
assert!(matches!(&error.kind, AssetErrorKind::UTF8));
|
||||||
|
assert!(format!("{error}").contains("UTF8 decoding error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_found_fallback_error() {
|
||||||
|
let error = fallback("not_found.png", &Graph::default()).unwrap_err();
|
||||||
|
|
||||||
|
assert!(matches!(&error.kind, AssetErrorKind::NotFound));
|
||||||
|
assert!(
|
||||||
|
format!("{error}")
|
||||||
|
.contains("The file was not found in the searched path")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[expect(clippy::panic_in_result_fn)]
|
||||||
|
mod serial_tests {
|
||||||
|
use std::{fs, os::unix::fs::PermissionsExt as _};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::dev::test::{Directories, Error};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn io_asset_error() -> Result<(), Error> {
|
||||||
|
let dirs = Directories::setup("io_asset_error")?;
|
||||||
|
|
||||||
|
let assets = dirs.assets.clone();
|
||||||
|
let file = assets.join("unreadable.png");
|
||||||
|
|
||||||
|
fs::write(&file, [1, 0, 1])?;
|
||||||
|
let mut permissions = fs::metadata(&file)?.permissions();
|
||||||
|
permissions.set_mode(0o200);
|
||||||
|
fs::set_permissions(&file, permissions)?;
|
||||||
|
|
||||||
|
let error = fallback("unreadable.png", &Graph::default()).unwrap_err();
|
||||||
|
|
||||||
|
assert!(matches!(&error.kind, AssetErrorKind::IO));
|
||||||
|
assert!(
|
||||||
|
format!("{error}")
|
||||||
|
.contains("was found, but it could not be loaded")
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,10 @@ use crate::syntax::content::{Parseable, parser::Lexeme};
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Verse {
|
pub struct Verse {
|
||||||
open: Option<bool>,
|
open: Option<bool>,
|
||||||
citation: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Verse {
|
impl Verse {
|
||||||
pub const fn new(open: bool) -> Verse {
|
pub const fn new(open: bool) -> Verse { Verse { open: Some(open) } }
|
||||||
Verse {
|
|
||||||
open: Some(open),
|
|
||||||
citation: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn probe_end(lexeme: &Lexeme) -> bool {
|
pub fn probe_end(lexeme: &Lexeme) -> bool {
|
||||||
lexeme.match_char_triple('\n', '&', '\n')
|
lexeme.match_char_triple('\n', '&', '\n')
|
||||||
|
|
@ -24,12 +18,7 @@ impl Parseable for Verse {
|
||||||
lexeme.match_char_triple('\n', '&', '\n')
|
lexeme.match_char_triple('\n', '&', '\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lex(_lexeme: &Lexeme) -> Verse {
|
fn lex(_lexeme: &Lexeme) -> Verse { Verse { open: None } }
|
||||||
Verse {
|
|
||||||
open: None,
|
|
||||||
citation: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&self) -> String {
|
fn render(&self) -> String {
|
||||||
if let Some(open) = self.open {
|
if let Some(open) = self.open {
|
||||||
|
|
@ -59,12 +48,44 @@ impl std::fmt::Display for Verse {
|
||||||
None => "unknown",
|
None => "unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
let citation = if self.citation.is_some() {
|
write!(f, "Verse [{display_open_state}]")
|
||||||
" cited"
|
}
|
||||||
} else {
|
}
|
||||||
""
|
|
||||||
};
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
write!(f, "Verse [{display_open_state}{citation}]")
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lexed_verse_is_empty() {
|
||||||
|
let verse = Verse::lex(&Lexeme::default());
|
||||||
|
assert!(verse.open.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flat_verse_is_empty() {
|
||||||
|
let verse = Verse::new(true);
|
||||||
|
assert!(verse.flatten().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "Attempt to render a verse tag while open state is unknown"
|
||||||
|
)]
|
||||||
|
fn render_attempt_with_unknown_open_state() {
|
||||||
|
let verse = Verse::lex(&Lexeme::default());
|
||||||
|
verse.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display() {
|
||||||
|
let open = Verse::new(true);
|
||||||
|
assert_eq!(format!("{open}"), "Verse [open]");
|
||||||
|
|
||||||
|
let closed = Verse::new(false);
|
||||||
|
assert_eq!(format!("{closed}"), "Verse [closed]");
|
||||||
|
|
||||||
|
let unknown = Verse::lex(&Lexeme::default());
|
||||||
|
assert_eq!(format!("{unknown}"), "Verse [unknown]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue