Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
supersurviveur committed May 31, 2024
1 parent e6198d6 commit 40b4427
Show file tree
Hide file tree
Showing 10 changed files with 454 additions and 42 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ If you encounter any issues, please report them on the [GitHub repository](https
Feel free to contribute to the project ! See the [CONTRIBUTING.md](./CONTRIBUTING.md) file for instructions on how to build the project.

# Acknowledgements
- Thanks to [Enter-tainer](https://github.com/Enter-tainer) for his advices
- Thanks to [Enter-tainer](https://github.com/Enter-tainer) for his advices
- Thanks to [Le-Foucheur](https://github.com/Le-Foucheur) for testing
1 change: 1 addition & 0 deletions typst-math-rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]
coverage = []

[dependencies]
wasm-bindgen = "0.2.84"
Expand Down
12 changes: 7 additions & 5 deletions typst-math-rust/coverage.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
#!/bin/sh

TEST=""

# Generate profile files
RUSTFLAGS="-C instrument-coverage -Z coverage-options=branch" cargo +nightly test --tests
RUSTFLAGS="-C instrument-coverage -Z coverage-options=branch" cargo +nightly test --tests --features coverage $TEST
cargo profdata -- merge -sparse default_*.profraw -o typst-math.profdata

# Get binary names
FILES=$( \
for file in \
$( \
RUSTFLAGS="-C instrument-coverage -Z coverage-options=branch" \
cargo +nightly test --tests --no-run --message-format=json \
cargo +nightly test --tests --features coverage $TEST --no-run --message-format=json \
| jq -r "select(.profile.test == true) | .filenames[]" \
| grep -v dSYM - \
); \
Expand All @@ -21,16 +23,16 @@ FILES=$( \
# Generate report
cargo cov -- report \
$FILES \
--use-color --ignore-filename-regex='/.cargo/registry' \
--use-color --ignore-filename-regex='/.cargo/registry|main.rs|rustc/' \
--instr-profile=typst-math.profdata

# Show where code isn't covered
# cargo cov -- show \
# $FILES \
# --use-color --ignore-filename-regex='/.cargo/registry' \
# --use-color --ignore-filename-regex='/.cargo/registry|main.rs|rustc/' \
# --instr-profile=typst-math.profdata --summary-only \
# --show-instantiations --show-line-counts-or-regions \
# --Xdemangler=rustfilt | less -R
# --Xdemangler=rustfilt --show-branches=percent | less -R

# Clean files
rm -f default_*.profraw
Expand Down
20 changes: 16 additions & 4 deletions typst-math-rust/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::utils::symbols::Color;
/// - rust side: in the decoraions hasmap
/// - js side: in the decorations array, to avoid generating the same decoration multiple times (Expensive)
#[derive(Debug, Clone)]
#[wasm_bindgen(getter_with_clone)]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen(getter_with_clone))]
pub struct Decoration {
pub uuid: String,
pub symbol: String,
Expand All @@ -20,7 +20,7 @@ pub struct Decoration {

/// Represents a symbol position in the document
#[derive(Debug, Clone)]
#[wasm_bindgen]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen)]
pub struct Position {
pub start: usize,
pub end: usize,
Expand All @@ -35,17 +35,29 @@ pub struct Options {
pub custom_symbols: HashMap<String, CustomSymbol>,
}

impl Default for Options {
fn default() -> Self {
Options {
rendering_mode: 3,
render_outside_math: true,
render_spaces: false,
blacklisted_symbols: vec![],
custom_symbols: HashMap::new(),
}
}
}

/// Represents a user defined symbol that can be used trough WASM
#[derive(Debug)]
#[wasm_bindgen(getter_with_clone)]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen(getter_with_clone))]
pub struct CustomSymbol {
pub name: String,
pub symbol: String,
pub category: String,
}

/// Represents the result of the parsing function
#[wasm_bindgen(getter_with_clone)]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen(getter_with_clone))]
pub struct Parsed {
pub decorations: Vec<Decoration>,
pub edit_start_line: usize,
Expand Down
54 changes: 39 additions & 15 deletions typst-math-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use utils::hook::set_panic_hook;
use wasm_bindgen::prelude::*;

/// Initialize the WASM library
#[wasm_bindgen]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen)]
pub fn init_lib() {
set_panic_hook();
}
Expand All @@ -34,7 +34,7 @@ pub fn find_node<'a>(
}

/// Parse a document and return the decorations to apply
#[wasm_bindgen]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen)]
pub fn parse_document(
content: &str,
edited_line_start: i32,
Expand Down Expand Up @@ -86,18 +86,13 @@ pub fn parse_document(
// Find all nodes in this range
find_node(range.clone(), root.clone(), &mut nodes);

if nodes.is_empty() {
// If no nodes were found, parse the entire document
nodes.push(root);
} else {
// Get the range of part which will be reparsed
let first = source.find(nodes.first().unwrap().span()).unwrap().range();
let last = source.find(nodes.last().unwrap().span()).unwrap().range();
edit_start_line = source.byte_to_line(first.start).unwrap();
edit_end_line = source.byte_to_line(last.end).unwrap();
edit_start_column = source.byte_to_column(first.start).unwrap();
edit_end_column = source.byte_to_column(last.end).unwrap();
}
// Get the range of part which will be reparsed
let first = source.find(nodes.first().unwrap().span()).unwrap().range();
let last = source.find(nodes.last().unwrap().span()).unwrap().range();
edit_start_line = source.byte_to_line(first.start).unwrap();
edit_end_line = source.byte_to_line(last.end).unwrap();
edit_start_column = source.byte_to_column(first.start).unwrap();
edit_end_column = source.byte_to_column(last.end).unwrap();
} else {
// Parse the entire document
let root = source.find(source.root().span()).unwrap();
Expand Down Expand Up @@ -149,11 +144,40 @@ pub fn parse_document(
}

/// Generate a custom symbol struct easily from JS
#[wasm_bindgen]
#[cfg_attr(not(feature = "coverage"), wasm_bindgen)]
pub fn generate_custom_symbol(name: String, symbol: String, category: String) -> CustomSymbol {
return CustomSymbol {
name,
symbol,
category,
};
}

#[cfg(test)]
mod tests {
use crate::{generate_custom_symbol, init_lib, parse_document};

#[test]
fn test_initialization() {
init_lib();
}

#[test]
fn test_custom_symbols() {
let parsed = parse_document(
"$alpha symbol$",
-1,
-1,
3,
true,
true,
vec![],
vec![generate_custom_symbol(
"symbol".to_string(),
"symbol".to_string(),
"operator".to_string(),
)],
);
assert_eq!(parsed.decorations.len(), 2);
}
}
35 changes: 26 additions & 9 deletions typst-math-rust/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ pub struct State {
pub is_attachment: bool,
}

impl Default for State {
fn default() -> Self {
State {
is_base: false,
is_attachment: false,
}
}
}

/// Use a recursive DFS to traverse the entire AST and apply style \
/// Most complex part of the code, match the current expression and then,
/// compute the appropriate style and/or if we need to continue over children
Expand Down Expand Up @@ -164,26 +173,32 @@ fn math_attach_block(parser: &mut InnerParser) {
if parser.options.rendering_mode > 1 {
parser.offset = (1, 0);
}
let top_decor = if parser.options.rendering_mode > 1 {
"font-size: 0.8em; transform: translateY(-30%); display: inline-block;"
let (top_decor, top_uuid) = if parser.options.rendering_mode > 1 {
(
"font-size: 0.8em; transform: translateY(-30%); display: inline-block;",
"top-",
)
} else {
""
("", "")
};
let bottom_decor = if parser.options.rendering_mode > 1 {
"font-size: 0.8em; transform: translateY(20%); display: inline-block;"
let (bottom_decor, bottom_uuid) = if parser.options.rendering_mode > 1 {
(
"font-size: 0.8em; transform: translateY(20%); display: inline-block;",
"bottom-",
)
} else {
""
("", "")
};
// Set state for top and bottom attachment
parser.state.is_base = false;
parser.state.is_attachment = parser.options.rendering_mode > 1;
if let Some(top) = attachment.top() {
let top = parser.expr.find(top.span()).unwrap();
ast_dfs(parser, &top, "top-", top_decor, parser.offset)
ast_dfs(parser, &top, top_uuid, top_decor, parser.offset)
}
if let Some(bottom) = attachment.bottom() {
let bottom = parser.expr.find(bottom.span()).unwrap();
ast_dfs(parser, &bottom, "bottom-", bottom_decor, parser.offset)
ast_dfs(parser, &bottom, bottom_uuid, bottom_decor, parser.offset)
}
// Restore the state
parser.state.is_base = state.is_base;
Expand All @@ -198,7 +213,7 @@ fn math_block(parser: &mut InnerParser) {
&& children[1].kind() == SyntaxKind::Math
&& children[2].kind() == SyntaxKind::RightParen
{
// This serie of check aims to verify that the block inside paren is 'simple', wich means that we can propagate style (So top and bottom attachment)
// This serie of checks aims to verify that the block inside paren is 'simple', wich means that we can propagate style (So top and bottom attachment)
let mut propagate_style = false;
let sub_children: Vec<LinkedNode> = children[1].children().collect();

Expand Down Expand Up @@ -259,6 +274,8 @@ fn math_block(parser: &mut InnerParser) {
return;
}
}
// Style isn't propagated, reset state
parser.state.is_attachment = false;
for child in parser.expr.children() {
ast_dfs(parser, &child, "", "", (0, 0)); // Propagate the function
}
Expand Down
113 changes: 111 additions & 2 deletions typst-math-rust/src/parser/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::{
},
};
use std::{collections::HashMap, ops::Range};
use typst_syntax::{ast::AstNode, LinkedNode, Source};
use typst_syntax::SyntaxNode;
use typst_syntax::{ast::AstNode, LinkedNode, Source};

/// Get symbol from it's name
pub fn get_symbol(content: String, options: &Options) -> Option<(Category, String)> {
Expand Down Expand Up @@ -49,7 +49,6 @@ fn len_utf16(string: &str) -> usize {
string.chars().map(char::len_utf16).sum()
}


/// Return the index range of the UTF-16 code unit at the byte index range. \
/// Faster than calling `byte_to_utf16` over start and end.
fn byte_range_to_utf16(source: &Source, range: &Range<usize>) -> Option<Range<usize>> {
Expand Down Expand Up @@ -207,3 +206,113 @@ impl<'a> InnerParser<'a> {
)
}
}

#[cfg(test)]
mod tests {
use typst_syntax::SyntaxNode;

use crate::{interface::Options, parser::parser::State};

#[test]
fn test_inner_parser() {
let source = typst_syntax::Source::detached("α");
let mut result = std::collections::HashMap::new();
let mut state = State::default();
let options = Options::default();
let node = SyntaxNode::leaf(typst_syntax::SyntaxKind::Ident, "alpha");
let expr = typst_syntax::LinkedNode::new(&node);
let mut parser = super::InnerParser::new(&source, &expr, &mut result, &mut state, &options);
parser.insert_result_symbol(
0..2,
"alpha".to_string(),
"alpha".to_string(),
"",
(0, 0),
("", ""),
);

let mut parser = super::InnerParser::from(&mut parser, &expr, "alpha", "", (0, 0));
parser.insert_result_symbol(
0..2,
"alpha".to_string(),
"alpha".to_string(),
"",
(0, 0),
("", ""),
);
assert_eq!(parser.result.len(), 1);
assert_eq!(parser.result.get("alpha").unwrap().symbol, "α");
}

#[test]
fn test_inner_parser_spaces() {
let source = typst_syntax::Source::detached("zwnj");
let mut result = std::collections::HashMap::new();
let mut state = State::default();
let mut options = Options::default();
let node = SyntaxNode::leaf(typst_syntax::SyntaxKind::MathIdent, "zwnj");
let expr = typst_syntax::LinkedNode::new(&node);
let mut parser = super::InnerParser::new(&source, &expr, &mut result, &mut state, &options);
parser.insert_result_symbol(
0..4,
"zwnj".to_string(),
"zwnj".to_string(),
"",
(0, 0),
("", ""),
);
assert_eq!(parser.result.len(), 0);

options.render_spaces = true;
let mut parser = super::InnerParser::new(&source, &expr, &mut result, &mut state, &options);
parser.insert_result_symbol(
0..4,
"zwnj".to_string(),
"zwnj".to_string(),
"",
(0, 0),
("", ""),
);
assert_eq!(parser.result.len(), 1);
}

#[test]
fn test_inner_parser_not_found() {
let source = typst_syntax::Source::detached("");
let mut result = std::collections::HashMap::new();
let mut state = State::default();
let options = Options::default();
let node = SyntaxNode::leaf(typst_syntax::SyntaxKind::Ident, "alpha");
let expr = typst_syntax::LinkedNode::new(&node);
let mut parser = super::InnerParser::new(&source, &expr, &mut result, &mut state, &options);
parser.insert_result_symbol(
0..5,
"doesn't exist".to_string(),
"doesn't exist".to_string(),
"",
(0, 0),
("", ""),
);
}

#[test]
fn test_inner_parser_blacklist() {
let source = typst_syntax::Source::detached("alpha");
let mut result = std::collections::HashMap::new();
let mut state = State::default();
let mut options = Options::default();
options.blacklisted_symbols.push("alpha".to_string());
let node = SyntaxNode::leaf(typst_syntax::SyntaxKind::Ident, "alpha");
let expr = typst_syntax::LinkedNode::new(&node);
let mut parser = super::InnerParser::new(&source, &expr, &mut result, &mut state, &options);
parser.insert_result_symbol(
0..5,
"alpha".to_string(),
"alpha".to_string(),
"",
(0, 0),
("", ""),
);
assert_eq!(parser.result.len(), 0);
}
}
Loading

0 comments on commit 40b4427

Please sign in to comment.