/ src / modules / powerrename / unittests / MetadataFormatHelperTests.cpp
MetadataFormatHelperTests.cpp
  1  #include "pch.h"
  2  #include "MetadataFormatHelper.h"
  3  #include <cmath>
  4  
  5  using namespace Microsoft::VisualStudio::CppUnitTestFramework;
  6  using namespace PowerRenameLib;
  7  
  8  namespace MetadataFormatHelperTests
  9  {
 10      TEST_CLASS(FormatApertureTests)
 11      {
 12      public:
 13          TEST_METHOD(FormatAperture_ValidValue)
 14          {
 15              // Test formatting a typical aperture value
 16              std::wstring result = MetadataFormatHelper::FormatAperture(2.8);
 17              Assert::AreEqual(L"f/2.8", result.c_str());
 18          }
 19  
 20          TEST_METHOD(FormatAperture_SmallValue)
 21          {
 22              // Test small aperture (large f-number)
 23              std::wstring result = MetadataFormatHelper::FormatAperture(1.4);
 24              Assert::AreEqual(L"f/1.4", result.c_str());
 25          }
 26  
 27          TEST_METHOD(FormatAperture_LargeValue)
 28          {
 29              // Test large aperture (small f-number)
 30              std::wstring result = MetadataFormatHelper::FormatAperture(22.0);
 31              Assert::AreEqual(L"f/22.0", result.c_str());
 32          }
 33  
 34          TEST_METHOD(FormatAperture_RoundedValue)
 35          {
 36              // Test rounding to one decimal place
 37              std::wstring result = MetadataFormatHelper::FormatAperture(5.66666);
 38              Assert::AreEqual(L"f/5.7", result.c_str());
 39          }
 40  
 41          TEST_METHOD(FormatAperture_Zero)
 42          {
 43              // Test zero value
 44              std::wstring result = MetadataFormatHelper::FormatAperture(0.0);
 45              Assert::AreEqual(L"f/0.0", result.c_str());
 46          }
 47      };
 48  
 49      TEST_CLASS(FormatShutterSpeedTests)
 50      {
 51      public:
 52          TEST_METHOD(FormatShutterSpeed_FastSpeed)
 53          {
 54              // Test fast shutter speed (fraction of second)
 55              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(0.002);
 56              Assert::AreEqual(L"1/500s", result.c_str());
 57          }
 58  
 59          TEST_METHOD(FormatShutterSpeed_VeryFastSpeed)
 60          {
 61              // Test very fast shutter speed
 62              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(0.0001);
 63              Assert::AreEqual(L"1/10000s", result.c_str());
 64          }
 65  
 66          TEST_METHOD(FormatShutterSpeed_SlowSpeed)
 67          {
 68              // Test slow shutter speed (more than 1 second)
 69              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(2.5);
 70              Assert::AreEqual(L"2.5s", result.c_str());
 71          }
 72  
 73          TEST_METHOD(FormatShutterSpeed_OneSecond)
 74          {
 75              // Test exactly 1 second
 76              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(1.0);
 77              Assert::AreEqual(L"1.0s", result.c_str());
 78          }
 79  
 80          TEST_METHOD(FormatShutterSpeed_VerySlowSpeed)
 81          {
 82              // Test very slow shutter speed (< 1 second but close)
 83              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(0.5);
 84              Assert::AreEqual(L"1/2s", result.c_str());
 85          }
 86  
 87          TEST_METHOD(FormatShutterSpeed_Zero)
 88          {
 89              // Test zero value
 90              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(0.0);
 91              Assert::AreEqual(L"0", result.c_str());
 92          }
 93  
 94          TEST_METHOD(FormatShutterSpeed_Negative)
 95          {
 96              // Test negative value (invalid but should handle gracefully)
 97              std::wstring result = MetadataFormatHelper::FormatShutterSpeed(-1.0);
 98              Assert::AreEqual(L"0", result.c_str());
 99          }
100      };
101  
102      TEST_CLASS(FormatISOTests)
103      {
104      public:
105          TEST_METHOD(FormatISO_TypicalValue)
106          {
107              // Test typical ISO value
108              std::wstring result = MetadataFormatHelper::FormatISO(400);
109              Assert::AreEqual(L"ISO 400", result.c_str());
110          }
111  
112          TEST_METHOD(FormatISO_LowValue)
113          {
114              // Test low ISO value
115              std::wstring result = MetadataFormatHelper::FormatISO(100);
116              Assert::AreEqual(L"ISO 100", result.c_str());
117          }
118  
119          TEST_METHOD(FormatISO_HighValue)
120          {
121              // Test high ISO value
122              std::wstring result = MetadataFormatHelper::FormatISO(12800);
123              Assert::AreEqual(L"ISO 12800", result.c_str());
124          }
125  
126          TEST_METHOD(FormatISO_Zero)
127          {
128              // Test zero value
129              std::wstring result = MetadataFormatHelper::FormatISO(0);
130              Assert::AreEqual(L"ISO", result.c_str());
131          }
132  
133          TEST_METHOD(FormatISO_Negative)
134          {
135              // Test negative value (invalid but should handle gracefully)
136              std::wstring result = MetadataFormatHelper::FormatISO(-100);
137              Assert::AreEqual(L"ISO", result.c_str());
138          }
139      };
140  
141      TEST_CLASS(FormatFlashTests)
142      {
143      public:
144          TEST_METHOD(FormatFlash_Off)
145          {
146              // Test flash off (bit 0 = 0)
147              std::wstring result = MetadataFormatHelper::FormatFlash(0x0);
148              Assert::AreEqual(L"Flash Off", result.c_str());
149          }
150  
151          TEST_METHOD(FormatFlash_On)
152          {
153              // Test flash on (bit 0 = 1)
154              std::wstring result = MetadataFormatHelper::FormatFlash(0x1);
155              Assert::AreEqual(L"Flash On", result.c_str());
156          }
157  
158          TEST_METHOD(FormatFlash_OnWithAdditionalFlags)
159          {
160              // Test flash on with additional flags
161              std::wstring result = MetadataFormatHelper::FormatFlash(0x5); // 0b0101 = fired, return detected
162              Assert::AreEqual(L"Flash On", result.c_str());
163          }
164  
165          TEST_METHOD(FormatFlash_OffWithAdditionalFlags)
166          {
167              // Test flash off with additional flags
168              std::wstring result = MetadataFormatHelper::FormatFlash(0x10); // Bit 0 is 0
169              Assert::AreEqual(L"Flash Off", result.c_str());
170          }
171      };
172  
173      TEST_CLASS(FormatCoordinateTests)
174      {
175      public:
176          TEST_METHOD(FormatCoordinate_NorthLatitude)
177          {
178              // Test north latitude
179              std::wstring result = MetadataFormatHelper::FormatCoordinate(40.7128, true);
180              Assert::AreEqual(L"40°42.77'N", result.c_str());
181          }
182  
183          TEST_METHOD(FormatCoordinate_SouthLatitude)
184          {
185              // Test south latitude
186              std::wstring result = MetadataFormatHelper::FormatCoordinate(-33.8688, true);
187              Assert::AreEqual(L"33°52.13'S", result.c_str());
188          }
189  
190          TEST_METHOD(FormatCoordinate_EastLongitude)
191          {
192              // Test east longitude
193              std::wstring result = MetadataFormatHelper::FormatCoordinate(151.2093, false);
194              Assert::AreEqual(L"151°12.56'E", result.c_str());
195          }
196  
197          TEST_METHOD(FormatCoordinate_WestLongitude)
198          {
199              // Test west longitude
200              std::wstring result = MetadataFormatHelper::FormatCoordinate(-74.0060, false);
201              Assert::AreEqual(L"74°0.36'W", result.c_str());
202          }
203  
204          TEST_METHOD(FormatCoordinate_ZeroLatitude)
205          {
206              // Test equator (0 degrees latitude)
207              std::wstring result = MetadataFormatHelper::FormatCoordinate(0.0, true);
208              Assert::AreEqual(L"0°0.00'N", result.c_str());
209          }
210  
211          TEST_METHOD(FormatCoordinate_ZeroLongitude)
212          {
213              // Test prime meridian (0 degrees longitude)
214              std::wstring result = MetadataFormatHelper::FormatCoordinate(0.0, false);
215              Assert::AreEqual(L"0°0.00'E", result.c_str());
216          }
217      };
218  
219      TEST_CLASS(FormatSystemTimeTests)
220      {
221      public:
222          TEST_METHOD(FormatSystemTime_ValidDateTime)
223          {
224              // Test formatting a valid date and time
225              SYSTEMTIME st = { 0 };
226              st.wYear = 2024;
227              st.wMonth = 3;
228              st.wDay = 15;
229              st.wHour = 14;
230              st.wMinute = 30;
231              st.wSecond = 45;
232  
233              std::wstring result = MetadataFormatHelper::FormatSystemTime(st);
234              Assert::AreEqual(L"2024-03-15 14:30:45", result.c_str());
235          }
236  
237          TEST_METHOD(FormatSystemTime_Midnight)
238          {
239              // Test midnight time
240              SYSTEMTIME st = { 0 };
241              st.wYear = 2024;
242              st.wMonth = 1;
243              st.wDay = 1;
244              st.wHour = 0;
245              st.wMinute = 0;
246              st.wSecond = 0;
247  
248              std::wstring result = MetadataFormatHelper::FormatSystemTime(st);
249              Assert::AreEqual(L"2024-01-01 00:00:00", result.c_str());
250          }
251  
252          TEST_METHOD(FormatSystemTime_EndOfDay)
253          {
254              // Test end of day time
255              SYSTEMTIME st = { 0 };
256              st.wYear = 2024;
257              st.wMonth = 12;
258              st.wDay = 31;
259              st.wHour = 23;
260              st.wMinute = 59;
261              st.wSecond = 59;
262  
263              std::wstring result = MetadataFormatHelper::FormatSystemTime(st);
264              Assert::AreEqual(L"2024-12-31 23:59:59", result.c_str());
265          }
266      };
267  
268      TEST_CLASS(ParseSingleRationalTests)
269      {
270      public:
271          TEST_METHOD(ParseSingleRational_ValidValue)
272          {
273              // Test parsing a valid rational: 5/2 = 2.5
274              uint8_t bytes[] = { 5, 0, 0, 0, 2, 0, 0, 0 };
275              double result = MetadataFormatHelper::ParseSingleRational(bytes, 0);
276              Assert::AreEqual(2.5, result, 0.001);
277          }
278  
279          TEST_METHOD(ParseSingleRational_IntegerResult)
280          {
281              // Test parsing rational that results in integer: 10/5 = 2.0
282              uint8_t bytes[] = { 10, 0, 0, 0, 5, 0, 0, 0 };
283              double result = MetadataFormatHelper::ParseSingleRational(bytes, 0);
284              Assert::AreEqual(2.0, result, 0.001);
285          }
286  
287          TEST_METHOD(ParseSingleRational_LargeNumerator)
288          {
289              // Test parsing with large numerator: 1000/100 = 10.0
290              uint8_t bytes[] = { 0xE8, 0x03, 0, 0, 100, 0, 0, 0 }; // 1000 in little-endian
291              double result = MetadataFormatHelper::ParseSingleRational(bytes, 0);
292              Assert::AreEqual(10.0, result, 0.001);
293          }
294  
295          TEST_METHOD(ParseSingleRational_ZeroDenominator)
296          {
297              // Test parsing with zero denominator (should return 0.0)
298              uint8_t bytes[] = { 5, 0, 0, 0, 0, 0, 0, 0 };
299              double result = MetadataFormatHelper::ParseSingleRational(bytes, 0);
300              Assert::AreEqual(0.0, result, 0.001);
301          }
302  
303          TEST_METHOD(ParseSingleRational_ZeroNumerator)
304          {
305              // Test parsing with zero numerator: 0/5 = 0.0
306              uint8_t bytes[] = { 0, 0, 0, 0, 5, 0, 0, 0 };
307              double result = MetadataFormatHelper::ParseSingleRational(bytes, 0);
308              Assert::AreEqual(0.0, result, 0.001);
309          }
310  
311          TEST_METHOD(ParseSingleRational_WithOffset)
312          {
313              // Test parsing with offset
314              uint8_t bytes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 10, 0, 0, 0, 5, 0, 0, 0 }; // Offset = 4
315              double result = MetadataFormatHelper::ParseSingleRational(bytes, 4);
316              Assert::AreEqual(2.0, result, 0.001);
317          }
318  
319          TEST_METHOD(ParseSingleRational_NullPointer)
320          {
321              // Test with null pointer (should return 0.0)
322              double result = MetadataFormatHelper::ParseSingleRational(nullptr, 0);
323              Assert::AreEqual(0.0, result, 0.001);
324          }
325      };
326  
327      TEST_CLASS(ParseSingleSRationalTests)
328      {
329      public:
330          TEST_METHOD(ParseSingleSRational_PositiveValue)
331          {
332              // Test parsing positive signed rational: 5/2 = 2.5
333              uint8_t bytes[] = { 5, 0, 0, 0, 2, 0, 0, 0 };
334              double result = MetadataFormatHelper::ParseSingleSRational(bytes, 0);
335              Assert::AreEqual(2.5, result, 0.001);
336          }
337  
338          TEST_METHOD(ParseSingleSRational_NegativeNumerator)
339          {
340              // Test parsing negative numerator: -5/2 = -2.5
341              uint8_t bytes[] = { 0xFB, 0xFF, 0xFF, 0xFF, 2, 0, 0, 0 }; // -5 in two's complement
342              double result = MetadataFormatHelper::ParseSingleSRational(bytes, 0);
343              Assert::AreEqual(-2.5, result, 0.001);
344          }
345  
346          TEST_METHOD(ParseSingleSRational_NegativeDenominator)
347          {
348              // Test parsing negative denominator: 5/-2 = -2.5
349              uint8_t bytes[] = { 5, 0, 0, 0, 0xFE, 0xFF, 0xFF, 0xFF }; // -2 in two's complement
350              double result = MetadataFormatHelper::ParseSingleSRational(bytes, 0);
351              Assert::AreEqual(-2.5, result, 0.001);
352          }
353  
354          TEST_METHOD(ParseSingleSRational_BothNegative)
355          {
356              // Test parsing both negative: -5/-2 = 2.5
357              uint8_t bytes[] = { 0xFB, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF };
358              double result = MetadataFormatHelper::ParseSingleSRational(bytes, 0);
359              Assert::AreEqual(2.5, result, 0.001);
360          }
361  
362          TEST_METHOD(ParseSingleSRational_ExposureBias)
363          {
364              // Test typical exposure bias value: -1/3 ≈ -0.333
365              uint8_t bytes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 3, 0, 0, 0 }; // -1/3
366              double result = MetadataFormatHelper::ParseSingleSRational(bytes, 0);
367              Assert::AreEqual(-0.333, result, 0.001);
368          }
369  
370          TEST_METHOD(ParseSingleSRational_ZeroDenominator)
371          {
372              // Test with zero denominator (should return 0.0)
373              uint8_t bytes[] = { 5, 0, 0, 0, 0, 0, 0, 0 };
374              double result = MetadataFormatHelper::ParseSingleSRational(bytes, 0);
375              Assert::AreEqual(0.0, result, 0.001);
376          }
377  
378          TEST_METHOD(ParseSingleSRational_NullPointer)
379          {
380              // Test with null pointer (should return 0.0)
381              double result = MetadataFormatHelper::ParseSingleSRational(nullptr, 0);
382              Assert::AreEqual(0.0, result, 0.001);
383          }
384      };
385  
386      TEST_CLASS(SanitizeForFileNameTests)
387      {
388      public:
389          TEST_METHOD(SanitizeForFileName_ValidString)
390          {
391              // Test string without illegal characters
392              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"Canon EOS 5D");
393              Assert::AreEqual(L"Canon EOS 5D", result.c_str());
394          }
395  
396          TEST_METHOD(SanitizeForFileName_WithColon)
397          {
398              // Test string with colon (illegal character)
399              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"Photo:001");
400              Assert::AreEqual(L"Photo_001", result.c_str());
401          }
402  
403          TEST_METHOD(SanitizeForFileName_WithSlashes)
404          {
405              // Test string with forward and backward slashes
406              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"Photos/2024\\January");
407              Assert::AreEqual(L"Photos_2024_January", result.c_str());
408          }
409  
410          TEST_METHOD(SanitizeForFileName_WithMultipleIllegalChars)
411          {
412              // Test string with multiple illegal characters
413              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"<Test>:File|Name*?.txt");
414              Assert::AreEqual(L"_Test__File_Name__.txt", result.c_str());
415          }
416  
417          TEST_METHOD(SanitizeForFileName_WithQuotes)
418          {
419              // Test string with quotes
420              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"Photo \"Best Shot\"");
421              Assert::AreEqual(L"Photo _Best Shot_", result.c_str());
422          }
423  
424          TEST_METHOD(SanitizeForFileName_WithTrailingDot)
425          {
426              // Test string with trailing dot (should be removed)
427              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"filename.");
428              Assert::AreEqual(L"filename", result.c_str());
429          }
430  
431          TEST_METHOD(SanitizeForFileName_WithTrailingSpace)
432          {
433              // Test string with trailing space (should be removed)
434              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"filename ");
435              Assert::AreEqual(L"filename", result.c_str());
436          }
437  
438          TEST_METHOD(SanitizeForFileName_WithMultipleTrailingDotsAndSpaces)
439          {
440              // Test string with multiple trailing dots and spaces
441              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"filename. . ");
442              Assert::AreEqual(L"filename", result.c_str());
443          }
444  
445          TEST_METHOD(SanitizeForFileName_WithControlCharacters)
446          {
447              // Test string with control characters
448              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"File\x01Name\x1F");
449              Assert::AreEqual(L"File_Name_", result.c_str());
450          }
451  
452          TEST_METHOD(SanitizeForFileName_EmptyString)
453          {
454              // Test empty string
455              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"");
456              Assert::AreEqual(L"", result.c_str());
457          }
458  
459          TEST_METHOD(SanitizeForFileName_OnlyIllegalCharacters)
460          {
461              // Test string with only illegal characters
462              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"<>:\"/\\|?*");
463              Assert::AreEqual(L"_________", result.c_str());
464          }
465  
466          TEST_METHOD(SanitizeForFileName_OnlyTrailingCharacters)
467          {
468              // Test string with only dots and spaces (should return empty)
469              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L". . ");
470              Assert::AreEqual(L"", result.c_str());
471          }
472  
473          TEST_METHOD(SanitizeForFileName_UnicodeCharacters)
474          {
475              // Test string with valid Unicode characters
476              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"照片_2024年");
477              Assert::AreEqual(L"照片_2024年", result.c_str());
478          }
479  
480          TEST_METHOD(SanitizeForFileName_MixedContent)
481          {
482              // Test realistic metadata string with multiple issues
483              std::wstring result = MetadataFormatHelper::SanitizeForFileName(L"Copyright © 2024: John/Jane Doe. ");
484              Assert::AreEqual(L"Copyright © 2024_ John_Jane Doe", result.c_str());
485          }
486      };
487  }