forked from
lewis.moe/bspds-sandbox
I've been saying "PDSes seem easy enough, they're what, some CRUD to a db? I can do that in my sleep". well i'm sleeping rn so let's go
1use image::{DynamicImage, ImageFormat};
2use std::io::Cursor;
3use tranquil_pds::image::{
4 DEFAULT_MAX_FILE_SIZE, ImageError, ImageProcessor, OutputFormat, THUMB_SIZE_FEED,
5 THUMB_SIZE_FULL,
6};
7
8fn create_test_png(width: u32, height: u32) -> Vec<u8> {
9 let img = DynamicImage::new_rgb8(width, height);
10 let mut buf = Vec::new();
11 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Png)
12 .unwrap();
13 buf
14}
15
16fn create_test_jpeg(width: u32, height: u32) -> Vec<u8> {
17 let img = DynamicImage::new_rgb8(width, height);
18 let mut buf = Vec::new();
19 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Jpeg)
20 .unwrap();
21 buf
22}
23
24fn create_test_gif(width: u32, height: u32) -> Vec<u8> {
25 let img = DynamicImage::new_rgb8(width, height);
26 let mut buf = Vec::new();
27 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Gif)
28 .unwrap();
29 buf
30}
31
32fn create_test_webp(width: u32, height: u32) -> Vec<u8> {
33 let img = DynamicImage::new_rgb8(width, height);
34 let mut buf = Vec::new();
35 img.write_to(&mut Cursor::new(&mut buf), ImageFormat::WebP)
36 .unwrap();
37 buf
38}
39
40#[test]
41fn test_format_support() {
42 let processor = ImageProcessor::new();
43
44 let png = create_test_png(500, 500);
45 let result = processor.process(&png, "image/png").unwrap();
46 assert_eq!(result.original.width, 500);
47 assert_eq!(result.original.height, 500);
48
49 let jpeg = create_test_jpeg(400, 300);
50 let result = processor.process(&jpeg, "image/jpeg").unwrap();
51 assert_eq!(result.original.width, 400);
52 assert_eq!(result.original.height, 300);
53
54 let gif = create_test_gif(200, 200);
55 let result = processor.process(&gif, "image/gif").unwrap();
56 assert_eq!(result.original.width, 200);
57
58 let webp = create_test_webp(300, 200);
59 let result = processor.process(&webp, "image/webp").unwrap();
60 assert_eq!(result.original.width, 300);
61}
62
63#[test]
64fn test_thumbnail_generation() {
65 let processor = ImageProcessor::new();
66
67 let small = create_test_png(100, 100);
68 let result = processor.process(&small, "image/png").unwrap();
69 assert!(
70 result.thumbnail_feed.is_none(),
71 "Small image should not get feed thumbnail"
72 );
73 assert!(
74 result.thumbnail_full.is_none(),
75 "Small image should not get full thumbnail"
76 );
77
78 let medium = create_test_png(500, 500);
79 let result = processor.process(&medium, "image/png").unwrap();
80 assert!(
81 result.thumbnail_feed.is_some(),
82 "Medium image should have feed thumbnail"
83 );
84 assert!(
85 result.thumbnail_full.is_none(),
86 "Medium image should NOT have full thumbnail"
87 );
88
89 let large = create_test_png(2000, 2000);
90 let result = processor.process(&large, "image/png").unwrap();
91 assert!(
92 result.thumbnail_feed.is_some(),
93 "Large image should have feed thumbnail"
94 );
95 assert!(
96 result.thumbnail_full.is_some(),
97 "Large image should have full thumbnail"
98 );
99 let thumb = result.thumbnail_feed.unwrap();
100 assert!(thumb.width <= THUMB_SIZE_FEED && thumb.height <= THUMB_SIZE_FEED);
101 let full = result.thumbnail_full.unwrap();
102 assert!(full.width <= THUMB_SIZE_FULL && full.height <= THUMB_SIZE_FULL);
103
104 let at_feed = create_test_png(THUMB_SIZE_FEED, THUMB_SIZE_FEED);
105 let above_feed = create_test_png(THUMB_SIZE_FEED + 1, THUMB_SIZE_FEED + 1);
106 assert!(
107 processor
108 .process(&at_feed, "image/png")
109 .unwrap()
110 .thumbnail_feed
111 .is_none()
112 );
113 assert!(
114 processor
115 .process(&above_feed, "image/png")
116 .unwrap()
117 .thumbnail_feed
118 .is_some()
119 );
120
121 let at_full = create_test_png(THUMB_SIZE_FULL, THUMB_SIZE_FULL);
122 let above_full = create_test_png(THUMB_SIZE_FULL + 1, THUMB_SIZE_FULL + 1);
123 assert!(
124 processor
125 .process(&at_full, "image/png")
126 .unwrap()
127 .thumbnail_full
128 .is_none()
129 );
130 assert!(
131 processor
132 .process(&above_full, "image/png")
133 .unwrap()
134 .thumbnail_full
135 .is_some()
136 );
137
138 let disabled = ImageProcessor::new().with_thumbnails(false);
139 let result = disabled.process(&large, "image/png").unwrap();
140 assert!(result.thumbnail_feed.is_none() && result.thumbnail_full.is_none());
141}
142
143#[test]
144fn test_output_format_conversion() {
145 let png = create_test_png(300, 300);
146 let jpeg = create_test_jpeg(300, 300);
147
148 let webp_proc = ImageProcessor::new().with_output_format(OutputFormat::WebP);
149 assert_eq!(
150 webp_proc
151 .process(&png, "image/png")
152 .unwrap()
153 .original
154 .mime_type,
155 "image/webp"
156 );
157
158 let jpeg_proc = ImageProcessor::new().with_output_format(OutputFormat::Jpeg);
159 assert_eq!(
160 jpeg_proc
161 .process(&png, "image/png")
162 .unwrap()
163 .original
164 .mime_type,
165 "image/jpeg"
166 );
167
168 let png_proc = ImageProcessor::new().with_output_format(OutputFormat::Png);
169 assert_eq!(
170 png_proc
171 .process(&jpeg, "image/jpeg")
172 .unwrap()
173 .original
174 .mime_type,
175 "image/png"
176 );
177}
178
179#[test]
180fn test_size_and_dimension_limits() {
181 assert_eq!(DEFAULT_MAX_FILE_SIZE, 10 * 1024 * 1024);
182
183 let max_dim = ImageProcessor::new().with_max_dimension(1000);
184 let large = create_test_png(2000, 2000);
185 let result = max_dim.process(&large, "image/png");
186 assert!(matches!(
187 result,
188 Err(ImageError::TooLarge {
189 width: 2000,
190 height: 2000,
191 max_dimension: 1000
192 })
193 ));
194
195 let max_file = ImageProcessor::new().with_max_file_size(100);
196 let data = create_test_png(500, 500);
197 let result = max_file.process(&data, "image/png");
198 assert!(matches!(
199 result,
200 Err(ImageError::FileTooLarge { max_size: 100, .. })
201 ));
202}
203
204#[test]
205fn test_error_handling() {
206 let processor = ImageProcessor::new();
207
208 let result = processor.process(b"this is not an image", "application/octet-stream");
209 assert!(matches!(result, Err(ImageError::UnsupportedFormat(_))));
210
211 let result = processor.process(b"\x89PNG\r\n\x1a\ncorrupted data here", "image/png");
212 assert!(matches!(result, Err(ImageError::DecodeError(_))));
213}
214
215#[test]
216fn test_aspect_ratio_preservation() {
217 let processor = ImageProcessor::new();
218
219 let landscape = create_test_png(1600, 800);
220 let result = processor.process(&landscape, "image/png").unwrap();
221 let thumb = result.thumbnail_full.unwrap();
222 let original_ratio = 1600.0 / 800.0;
223 let thumb_ratio = thumb.width as f64 / thumb.height as f64;
224 assert!((original_ratio - thumb_ratio).abs() < 0.1);
225
226 let portrait = create_test_png(800, 1600);
227 let result = processor.process(&portrait, "image/png").unwrap();
228 let thumb = result.thumbnail_full.unwrap();
229 let original_ratio = 800.0 / 1600.0;
230 let thumb_ratio = thumb.width as f64 / thumb.height as f64;
231 assert!((original_ratio - thumb_ratio).abs() < 0.1);
232}
233
234#[test]
235fn test_utilities_and_builder() {
236 assert!(ImageProcessor::is_supported_mime_type("image/jpeg"));
237 assert!(ImageProcessor::is_supported_mime_type("image/jpg"));
238 assert!(ImageProcessor::is_supported_mime_type("image/png"));
239 assert!(ImageProcessor::is_supported_mime_type("image/gif"));
240 assert!(ImageProcessor::is_supported_mime_type("image/webp"));
241 assert!(ImageProcessor::is_supported_mime_type("IMAGE/PNG"));
242 assert!(ImageProcessor::is_supported_mime_type("Image/Jpeg"));
243 assert!(!ImageProcessor::is_supported_mime_type("image/bmp"));
244 assert!(!ImageProcessor::is_supported_mime_type("image/tiff"));
245 assert!(!ImageProcessor::is_supported_mime_type("text/plain"));
246
247 let data = create_test_png(100, 100);
248 let processor = ImageProcessor::new();
249 let result = processor.process(&data, "application/octet-stream");
250 assert!(result.is_ok(), "Should detect PNG format from data");
251
252 let jpeg = create_test_jpeg(100, 100);
253 let stripped = ImageProcessor::strip_exif(&jpeg).unwrap();
254 assert!(!stripped.is_empty());
255
256 let processor = ImageProcessor::new()
257 .with_max_dimension(2048)
258 .with_max_file_size(5 * 1024 * 1024)
259 .with_output_format(OutputFormat::Jpeg)
260 .with_thumbnails(true);
261 let data = create_test_png(500, 500);
262 let result = processor.process(&data, "image/png").unwrap();
263 assert_eq!(result.original.mime_type, "image/jpeg");
264 assert!(!result.original.data.is_empty());
265 assert!(result.original.width > 0 && result.original.height > 0);
266}