use super::{
diagram::render_cut_diagram,
html::{base64_encode, cutting_sheet_html, html_escape, material_header_text},
};
use indicatrix::{
geometry::meet_solver::{Block, classify_blocks},
optics::materials::GemMaterial,
};
use indicatrix_cut_core::{ConstraintTier, Design, MaterialSelection, PreformSpec, ScheduleMeta};
fn round_brilliant_design() -> Design {
Design::new(
PreformSpec::block(2.0, 1.0, 2.0),
ScheduleMeta::standard_round_brilliant(),
ConstraintTier::standard_round_brilliant(),
)
}
fn custom_garnet(n_d: f32) -> GemMaterial {
let mut gem = GemMaterial::diamond();
gem.name = "My Garnet".to_string();
gem.dispersion = indicatrix::optics::dispersion::DispersionModel::Cauchy {
a: n_d,
b: 0.0,
c: 0.0,
};
gem
}
#[test]
fn cutting_sheet_html_prints_a_custom_materials_own_refractive_index() {
let mut design = round_brilliant_design();
design.material = MaterialSelection {
name: Some("My Garnet".to_string()),
specific_gravity_override: None,
refractive_index_override: None,
};
let solved = design.solve().expect("every tier is pinned");
let custom = [custom_garnet(1.9)];
let with_catalogue = cutting_sheet_html(&design, &solved, None, &custom);
assert!(
with_catalogue.contains("<td>1.9000</td>"),
"{with_catalogue}"
);
let without_catalogue = cutting_sheet_html(&design, &solved, None, &[]);
assert!(
without_catalogue.contains(&format!("<td>{:.4}</td>", design.meta.refractive_index)),
"{without_catalogue}"
);
}
#[test]
fn material_header_text_shows_a_guess_for_an_unnamed_design_near_a_builtin() {
let mut design = round_brilliant_design();
design.meta.refractive_index = 1.76;
design.material = MaterialSelection::none();
let n_d = design.effective_refractive_index_with(&[]);
assert_eq!(
material_header_text(&design, n_d),
"Sapphire? (from RI 1.76)"
);
}
#[test]
fn material_header_text_prints_the_name_plainly_once_set() {
let mut design = round_brilliant_design();
design.material = MaterialSelection {
name: Some("Sapphire".to_string()),
specific_gravity_override: None,
refractive_index_override: None,
};
let n_d = design.effective_refractive_index_with(&[]);
assert_eq!(material_header_text(&design, n_d), "Sapphire");
}
#[test]
fn cutting_sheet_html_contains_expected_rows() {
let design = round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let html = cutting_sheet_html(&design, &solved, None, &[]);
assert!(html.contains("<h1>Cutting Sheet</h1>"));
assert!(html.contains("Table"));
assert!(html.contains("Crown Main"));
assert!(html.contains("Pavilion Main"));
assert!(html.contains("Culet"));
let tbody_start = html.find("<tbody>").expect("tbody present") + "<tbody>".len();
let tbody_end = html.find("</tbody>").expect("tbody closes");
let tr_count = html[tbody_start..tbody_end].matches("<tr>").count();
assert_eq!(tr_count, design.tiers.len());
}
#[test]
fn elevation_column_prints_the_unsigned_angle_magnitude() {
let design = round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let html = cutting_sheet_html(&design, &solved, None, &[]);
assert!(html.contains("<th>Elevation</th>"), "{html}");
assert!(html.contains("<td>41.00°</td>"), "{html}");
}
#[test]
fn carat_weight_row_appears_only_when_girdle_diameter_is_set() {
let design_no_mm = round_brilliant_design();
let solved = design_no_mm.solve().expect("every tier is pinned");
let html_no_mm = cutting_sheet_html(&design_no_mm, &solved, None, &[]);
assert!(!html_no_mm.contains("Carat weight"));
let mut design_with_mm = round_brilliant_design();
design_with_mm.girdle_diameter_mm = Some(6.5);
design_with_mm.material.name = Some("Diamond".to_string());
let html_with_mm = cutting_sheet_html(&design_with_mm, &solved, None, &[]);
assert!(
html_with_mm.contains("Carat weight (estimate)"),
"{html_with_mm}"
);
}
#[test]
fn mm_column_appears_only_when_girdle_diameter_is_set() {
let design_no_mm = round_brilliant_design();
let solved = design_no_mm.solve().expect("every tier is pinned");
let html_no_mm = cutting_sheet_html(&design_no_mm, &solved, None, &[]);
assert!(!html_no_mm.contains("Mast (mm)"));
let mut design_with_mm = round_brilliant_design();
design_with_mm.girdle_diameter_mm = Some(6.5);
let solved_mm = design_with_mm.solve().expect("every tier is pinned");
let html_with_mm = cutting_sheet_html(&design_with_mm, &solved_mm, None, &[]);
assert!(html_with_mm.contains("Mast (mm)"));
assert!(html_with_mm.contains("Girdle diameter"));
}
#[test]
fn block_labels_match_classify_blocks_not_angle_sign() {
let design = round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let blocks = classify_blocks(&design.meet_tier_inputs());
assert_eq!(
blocks,
vec![
Block::Crown,
Block::Crown,
Block::Crown,
Block::Crown,
Block::Girdle,
Block::Pavilion,
Block::Pavilion,
Block::Pavilion,
]
);
let html = cutting_sheet_html(&design, &solved, None, &[]);
let culet_row_start = html.find("Culet").expect("Culet row present");
let row_html = &html[culet_row_start..culet_row_start + 400.min(html.len() - culet_row_start)];
assert!(
row_html.contains("block-pavilion"),
"Culet's row must be labelled Pavilion, got: {row_html}"
);
}
#[test]
fn render_cut_diagram_produces_a_sized_png_for_a_closed_design() {
let design = round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let image = render_cut_diagram(&design, &solved, 200, 100)
.expect("standard round brilliant closes to a solid");
assert_eq!(image.width, 200);
assert_eq!(image.height, 100);
assert_ne!(image.png_bytes, [] as [u8; 0]);
assert_eq!(&image.png_bytes[..8], &[137, 80, 78, 71, 13, 10, 26, 10]);
}
#[test]
fn diagram_embedding_switches_on_the_option() {
const NOTICE: &str = "<p class=\"diagram-missing\">";
let design = round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let image = render_cut_diagram(&design, &solved, 64, 32).expect("closes to a solid");
let with_image = cutting_sheet_html(&design, &solved, Some(&image), &[]);
assert!(with_image.contains("data:image/png;base64,"));
assert!(!with_image.contains(NOTICE));
let without_image = cutting_sheet_html(&design, &solved, None, &[]);
assert!(without_image.contains(NOTICE));
assert!(!without_image.contains("data:image/png;base64,"));
}
#[test]
fn base64_encode_matches_known_vectors() {
assert_eq!(base64_encode(b""), "");
assert_eq!(base64_encode(b"f"), "Zg==");
assert_eq!(base64_encode(b"fo"), "Zm8=");
assert_eq!(base64_encode(b"foo"), "Zm9v");
assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
}
#[test]
fn html_escape_handles_unsafe_characters() {
assert_eq!(
html_escape("A & B <tag> \"quoted\" 'apos'"),
"A & B <tag> "quoted" 'apos'"
);
}