#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test;
use fulgur_chart::font::DEFAULT_FONT;
use fulgur_chart::frontend::chartjs;
use fulgur_chart::raster_direct::{render_chart_to_png_default, render_chart_to_webp};
use fulgur_chart::render::render_chart;
fn fnv1a(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for &b in bytes {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
fn sample_spec() -> fulgur_chart::ir::ChartSpec {
let json = r#"{
"type": "bar",
"data": {
"labels": ["Mon", "Tue", "Wed", "Thu"],
"datasets": [{
"label": "Sales",
"backgroundColor": "rgba(54, 162, 235, 0.7)",
"borderColor": "rgb(54, 162, 235)",
"data": [12, 19, 7, 15]
}]
}
}"#;
chartjs::parse(json, false).expect("spec parses")
}
const PNG_SCALE: f32 = 2.0;
const PNG_SIGNATURE: &[u8; 8] = &[0x89, b'P', b'N', b'G', b'\r', b'\n', 0x1a, b'\n'];
const PNG_WIDTH: u32 = 1600;
const PNG_HEIGHT: u32 = 900;
const SVG_LEN: usize = 3483;
const SVG_HASH: u64 = 0x3af5_841b_b6bb_3b8e;
#[cfg(target_arch = "wasm32")]
const PNG_LEN_LINUX_X86: usize = 42630;
#[cfg(target_arch = "wasm32")]
const PNG_HASH_LINUX_X86: u64 = 0x6ee4_ac9e_6d16_b2e2;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
fn svg_is_byte_identical_across_platforms() {
let svg = render_chart(&sample_spec());
assert!(svg.starts_with("<svg"), "出力が SVG ではない");
assert_eq!(svg.len(), SVG_LEN, "SVG 長が期待値と不一致");
assert_eq!(
fnv1a(svg.as_bytes()),
SVG_HASH,
"SVG byte が期待値と不一致(cross-platform 決定論の破れ)"
);
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
fn png_renders_validly_on_every_platform() {
let png = render_chart_to_png_default(&sample_spec(), PNG_SCALE).expect("PNG 生成成功");
assert_eq!(&png[..8], PNG_SIGNATURE, "PNG シグネチャ不正");
let pix = tiny_skia::Pixmap::decode_png(&png).expect("生成 PNG がデコード可能");
assert_eq!(
(pix.width(), pix.height()),
(PNG_WIDTH, PNG_HEIGHT),
"PNG 寸法が期待値と不一致"
);
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
fn png_is_byte_identical_to_linux_x86_native() {
let png = render_chart_to_png_default(&sample_spec(), PNG_SCALE).expect("PNG 生成成功");
assert_eq!(
png.len(),
PNG_LEN_LINUX_X86,
"PNG 長が linux-x86 native と不一致"
);
assert_eq!(
fnv1a(&png),
PNG_HASH_LINUX_X86,
"PNG byte が linux-x86 native と不一致"
);
}
fn sample_stamp_spec() -> fulgur_chart::ir::ChartSpec {
let pts: Vec<String> = (0..200)
.map(|i| format!(r#"{{"x":{},"y":{}}}"#, i, (i * 37 + 13) % 100))
.collect();
let json = format!(
r#"{{"type":"scatter","data":{{"datasets":[{{"label":"d","data":[{}]}}]}}}}"#,
pts.join(",")
);
chartjs::parse(&json, false).expect("stamp spec parses")
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
fn stamp_png_renders_validly_and_deterministically() {
let spec = sample_stamp_spec();
let png = render_chart_to_png_default(&spec, PNG_SCALE).expect("stamp PNG 生成成功");
let png2 = render_chart_to_png_default(&spec, PNG_SCALE).expect("stamp PNG 生成成功(2回目)");
assert_eq!(
png, png2,
"stamp 経路 PNG が決定的でない(同一入力で byte 不一致)"
);
assert_eq!(&png[..8], PNG_SIGNATURE, "PNG シグネチャ不正");
let pix = tiny_skia::Pixmap::decode_png(&png).expect("生成 PNG がデコード可能");
assert_eq!(
(pix.width(), pix.height()),
(PNG_WIDTH, PNG_HEIGHT),
"stamp PNG 寸法が期待値と不一致"
);
}
#[cfg(target_arch = "wasm32")]
const STAMP_PNG_LEN_LINUX_X86: usize = 133447;
#[cfg(target_arch = "wasm32")]
const STAMP_PNG_HASH_LINUX_X86: u64 = 0x96f2_1e67_6ffb_146d;
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen_test]
fn stamp_png_byte_identical_to_linux_x86_native() {
let png =
render_chart_to_png_default(&sample_stamp_spec(), PNG_SCALE).expect("stamp PNG 生成成功");
assert_eq!(
png.len(),
STAMP_PNG_LEN_LINUX_X86,
"stamp PNG 長が linux-x86 native と不一致"
);
assert_eq!(
fnv1a(&png),
STAMP_PNG_HASH_LINUX_X86,
"stamp PNG byte が linux-x86 native と不一致"
);
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), test)]
fn stamp_webp_renders_validly_and_deterministically() {
let spec = sample_stamp_spec();
let a = render_chart_to_webp(&spec, PNG_SCALE, DEFAULT_FONT).expect("stamp WebP 生成成功");
let b =
render_chart_to_webp(&spec, PNG_SCALE, DEFAULT_FONT).expect("stamp WebP 生成成功(2回目)");
assert_eq!(
a, b,
"stamp 経路 WebP が決定的でない(同一入力で byte 不一致)"
);
assert!(a.len() > 12, "WebP が短すぎる");
assert_eq!(&a[0..4], b"RIFF", "WebP RIFF ヘッダ不正");
assert_eq!(&a[8..12], b"WEBP", "WebP fourcc 不正");
let img = image::load_from_memory_with_format(&a, image::ImageFormat::WebP)
.expect("生成 WebP がデコード可能")
.to_rgba8();
assert_eq!(
(img.width(), img.height()),
(PNG_WIDTH, PNG_HEIGHT),
"WebP 寸法が期待値と不一致"
);
}