/ src / common / UnitTests-CommonLib / Settings.Tests.cpp
Settings.Tests.cpp
  1  #include "pch.h"
  2  #include <common/SettingsAPI/settings_objects.h>
  3  
  4  #include "version/helper.h"
  5  
  6  using namespace Microsoft::VisualStudio::CppUnitTestFramework;
  7  using namespace PowerToysSettings;
  8  
  9  namespace UnitTestsCommonLib
 10  {
 11      void compareJsons(const json::JsonObject& expected, const json::JsonObject& actual, bool recursive = true)
 12      {
 13          auto iter = expected.First();
 14          while (iter.HasCurrent())
 15          {
 16              const auto key = iter.Current().Key();
 17              Assert::IsTrue(actual.HasKey(key));
 18  
 19              const std::wstring expectedStringified = iter.Current().Value().Stringify().c_str();
 20              const std::wstring actualStringified = actual.GetNamedValue(key).Stringify().c_str();
 21  
 22              if (recursive)
 23              {
 24                  json::JsonObject expectedJson;
 25                  if (json::JsonObject::TryParse(expectedStringified, expectedJson))
 26                  {
 27                      json::JsonObject actualJson;
 28                      if (json::JsonObject::TryParse(actualStringified, actualJson))
 29                      {
 30                          compareJsons(expectedJson, actualJson, true);
 31                      }
 32                      else
 33                      {
 34                          Assert::IsTrue(false);
 35                      }
 36                  }
 37                  else
 38                  {
 39                      Assert::AreEqual(expectedStringified, actualStringified);
 40                  }
 41              }
 42              else
 43              {
 44                  Assert::AreEqual(expectedStringified, actualStringified);
 45              }
 46  
 47              iter.MoveNext();
 48          }
 49      }
 50  
 51      TEST_CLASS (PowerToyValuesUnitTests)
 52      {
 53      private:
 54          const std::wstring m_json = L"{\"name\":\"Module Name\",\"properties\" : {\"bool_toggle_true\":{\"value\":true},\"bool_toggle_false\":{\"value\":false},\"color_picker\" : {\"value\":\"#ff8d12\"},\"int_spinner\" : {\"value\":10},\"string_text\" : {\"value\":\"a quick fox\"}},\"version\" : \"1.0\" }";
 55          const std::wstring m_moduleName = L"Module Name";
 56          const std::wstring m_moduleKey = L"Module Key";
 57  
 58      public:
 59          TEST_METHOD (LoadFromJsonBoolTrue)
 60          {
 61              PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
 62              auto value = values.get_bool_value(L"bool_toggle_true");
 63              Assert::IsTrue(value.has_value());
 64              Assert::AreEqual(true, *value);
 65          }
 66  
 67          TEST_METHOD (LoadFromJsonBoolFalse)
 68          {
 69              PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
 70              auto value = values.get_bool_value(L"bool_toggle_false");
 71              Assert::IsTrue(value.has_value());
 72              Assert::AreEqual(false, *value);
 73          }
 74  
 75          TEST_METHOD (LoadFromJsonInt)
 76          {
 77              PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
 78              auto value = values.get_int_value(L"int_spinner");
 79              Assert::IsTrue(value.has_value());
 80              Assert::AreEqual(10, *value);
 81          }
 82  
 83          TEST_METHOD (LoadFromJsonString)
 84          {
 85              PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
 86              auto value = values.get_string_value(L"string_text");
 87  
 88              Assert::IsTrue(value.has_value());
 89              std::wstring expected = L"a quick fox";
 90              Assert::AreEqual(expected, *value);
 91          }
 92  
 93          TEST_METHOD (LoadFromJsonColorPicker)
 94          {
 95              PowerToyValues values = PowerToyValues::from_json_string(m_json, m_moduleKey);
 96              auto value = values.get_string_value(L"color_picker");
 97  
 98              Assert::IsTrue(value.has_value());
 99              std::wstring expected = L"#ff8d12";
100              Assert::AreEqual(expected, *value);
101          }
102  
103          TEST_METHOD (LoadFromEmptyString)
104          {
105              auto func = [] { PowerToyValues values = PowerToyValues::from_json_string(L"", L"Module Key"); };
106              Assert::ExpectException<winrt::hresult_error>(func);
107          }
108  
109          TEST_METHOD (LoadFromInvalidString_NameMissed)
110          {
111              auto func = [] { PowerToyValues values = PowerToyValues::from_json_string(L"{\"properties\" : {\"bool_toggle_true\":{\"value\":true},\"bool_toggle_false\":{\"value\":false},\"color_picker\" : {\"value\":\"#ff8d12\"},\"int_spinner\" : {\"value\":10},\"string_text\" : {\"value\":\"a quick fox\"}},\"version\" : \"1.0\" }", L"Module Key"); };
112              Assert::ExpectException<winrt::hresult_error>(func);
113          }
114  
115          TEST_METHOD (LoadFromInvalidString_VersionMissed)
116          {
117              PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {}}", L"Module Key");
118              const std::wstring expectedStr = L"{\"name\" : \"Module Name\", \"properties\" : {},\"version\" : \"1.0\"}";
119              const auto expected = json::JsonObject::Parse(expectedStr);
120              const auto actual = json::JsonObject::Parse(values.serialize());
121  
122              compareJsons(expected, actual);
123          }
124  
125          TEST_METHOD (LoadFromInvalidString_PropertiesMissed)
126          {
127              PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"version\" : \"1.0\" }", L"Module Key");
128              const std::wstring expectedStr = L"{\"name\":\"Module Name\",\"version\" : \"1.0\" }";
129              const auto expected = json::JsonObject::Parse(expectedStr);
130              const auto actual = json::JsonObject::Parse(values.serialize());
131  
132              compareJsons(expected, actual);
133          }
134  
135          TEST_METHOD (LoadFromValidString_EmptyProperties)
136          {
137              PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {}, \"version\" : \"1.0\" }", L"Module Key");
138              const std::wstring expectedStr = L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"1.0\" }";
139              const auto expected = json::JsonObject::Parse(expectedStr);
140              const auto actual = json::JsonObject::Parse(values.serialize());
141  
142              compareJsons(expected, actual);
143          }
144  
145          TEST_METHOD (LoadFromValidString_ChangedVersion)
146          {
147              PowerToyValues values = PowerToyValues::from_json_string(L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"2.0\"}", L"Module Key");
148              const std::wstring expectedStr = L"{\"name\" : \"Module Name\", \"properties\" : {},\"version\" : \"1.0\"}"; //version from input json is ignored
149  
150              const auto expected = json::JsonObject::Parse(expectedStr);
151              const auto actual = json::JsonObject::Parse(values.serialize());
152  
153              compareJsons(expected, actual);
154          }
155  
156          TEST_METHOD (CreateWithName)
157          {
158              PowerToyValues values(m_moduleName, m_moduleKey);
159              const std::wstring expectedStr = L"{\"name\":\"Module Name\",\"properties\" : {},\"version\" : \"1.0\" }";
160  
161              const auto expected = json::JsonObject::Parse(expectedStr);
162              const auto actual = json::JsonObject::Parse(values.serialize());
163  
164              compareJsons(expected, actual);
165          }
166  
167          TEST_METHOD (AddPropertyBoolPositive)
168          {
169              PowerToyValues values(m_moduleName, m_moduleKey);
170              values.add_property<bool>(L"positive_bool_value", true);
171  
172              auto value = values.get_bool_value(L"positive_bool_value");
173              Assert::IsTrue(value.has_value());
174              Assert::AreEqual(true, *value);
175          }
176  
177          TEST_METHOD (AddPropertyBoolNegative)
178          {
179              PowerToyValues values(m_moduleName, m_moduleKey);
180              values.add_property<bool>(L"negative_bool_value", false);
181  
182              auto value = values.get_bool_value(L"negative_bool_value");
183              Assert::IsTrue(value.has_value());
184              Assert::AreEqual(false, *value);
185          }
186  
187          TEST_METHOD (AddPropertyIntPositive)
188          {
189              PowerToyValues values(m_moduleName, m_moduleKey);
190              const int intVal = 4392854;
191              values.add_property<int>(L"integer", intVal);
192  
193              auto value = values.get_int_value(L"integer");
194              Assert::IsTrue(value.has_value());
195              Assert::AreEqual(intVal, *value);
196          }
197  
198          TEST_METHOD (AddPropertyIntNegative)
199          {
200              PowerToyValues values(m_moduleName, m_moduleKey);
201              const int intVal = -4392854;
202              values.add_property<int>(L"integer", intVal);
203  
204              auto value = values.get_int_value(L"integer");
205              Assert::IsTrue(value.has_value());
206              Assert::AreEqual(intVal, *value);
207          }
208  
209          TEST_METHOD (AddPropertyIntZero)
210          {
211              PowerToyValues values(m_moduleName, m_moduleKey);
212              const int intVal = 0;
213              values.add_property<int>(L"integer", intVal);
214  
215              auto value = values.get_int_value(L"integer");
216              Assert::IsTrue(value.has_value());
217              Assert::AreEqual(intVal, *value);
218          }
219  
220          TEST_METHOD (AddPropertyStringEmpty)
221          {
222              PowerToyValues values(m_moduleName, m_moduleKey);
223              const std::wstring stringVal = L"";
224              values.add_property<std::wstring>(L"stringval", stringVal);
225  
226              auto value = values.get_string_value(L"stringval");
227              Assert::IsTrue(value.has_value());
228              Assert::AreEqual(stringVal, *value);
229          }
230  
231          TEST_METHOD (AddPropertyString)
232          {
233              PowerToyValues values(m_moduleName, m_moduleKey);
234              const std::wstring stringVal = L"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
235              values.add_property<std::wstring>(L"stringval", stringVal);
236  
237              auto value = values.get_string_value(L"stringval");
238              Assert::IsTrue(value.has_value());
239              Assert::AreEqual(stringVal, *value);
240          }
241  
242          TEST_METHOD (AddPropertyJsonEmpty)
243          {
244              PowerToyValues values(m_moduleName, m_moduleKey);
245              const auto json = json::JsonObject();
246              values.add_property<json::JsonObject>(L"jsonval", json);
247  
248              auto value = values.get_json(L"jsonval");
249              Assert::IsTrue(value.has_value());
250              compareJsons(json, *value);
251          }
252  
253          TEST_METHOD (AddPropertyJsonObject)
254          {
255              PowerToyValues values(m_moduleName, m_moduleKey);
256              const auto json = json::JsonObject::Parse(m_json);
257              values.add_property<json::JsonObject>(L"jsonval", json);
258  
259              auto value = values.get_json(L"jsonval");
260              Assert::IsTrue(value.has_value());
261              compareJsons(json, *value);
262          }
263      };
264  
265      TEST_CLASS (SettingsUnitTests)
266      {
267      private:
268          const std::wstring m_moduleName = L"Module Name";
269          const std::wstring m_defaultSettingsName = L"Default setting name";
270          const std::wstring m_defaultSettingsDescription = L"Default setting description";
271          const json::JsonObject m_defaultSettingsJson = json::JsonObject::Parse(L"{\"name\" : \"Module Name\", \"properties\" : {},\"version\" : \"1.0\"}");
272  
273          json::JsonObject createSettingsProperties(const std::wstring& editorType)
274          {
275              json::JsonObject properties = json::JsonObject();
276              properties.SetNamedValue(L"display_name", json::JsonValue::CreateStringValue(m_defaultSettingsDescription));
277              properties.SetNamedValue(L"editor_type", json::JsonValue::CreateStringValue(editorType));
278              properties.SetNamedValue(L"order", json::JsonValue::CreateNumberValue(1));
279              return properties;
280          }
281  
282      public:
283          TEST_METHOD (SettingsSerialization)
284          {
285              Settings settings(nullptr, m_moduleName);
286  
287              const auto expected = m_defaultSettingsJson;
288              const auto actual = json::JsonObject::Parse(settings.serialize());
289              compareJsons(expected, actual);
290          }
291  
292          TEST_METHOD (SettingsSerializationToBuffer)
293          {
294              Settings settings(nullptr, m_moduleName);
295  
296              const auto expected = m_defaultSettingsJson;
297              int expectedSize = expected.Stringify().size() + 1;
298  
299              int actualSize = expectedSize;
300              wchar_t* buffer = new wchar_t[expectedSize];
301              bool serializationSuccess = settings.serialize_to_buffer(buffer, &actualSize);
302  
303              Assert::IsTrue(serializationSuccess);
304              Assert::AreEqual(expectedSize, actualSize);
305  
306              auto actualJson = json::JsonObject::Parse(std::wstring(buffer));
307  
308              compareJsons(m_defaultSettingsJson, actualJson);
309          }
310  
311          TEST_METHOD (SettingsSetDescription)
312          {
313              const auto value = L"description value";
314              Settings settings(nullptr, m_moduleName);
315              settings.set_description(value);
316  
317              const auto expected = m_defaultSettingsJson;
318              expected.SetNamedValue(L"description", json::JsonValue::CreateStringValue(value));
319              const auto actual = json::JsonObject::Parse(settings.serialize());
320  
321              compareJsons(expected, actual);
322          }
323  
324          TEST_METHOD (SettingsSetIconKey)
325          {
326              const auto value = L"icon key";
327              Settings settings(nullptr, m_moduleName);
328              settings.set_icon_key(value);
329  
330              const auto expected = m_defaultSettingsJson;
331              expected.SetNamedValue(L"icon_key", json::JsonValue::CreateStringValue(value));
332              const auto actual = json::JsonObject::Parse(settings.serialize());
333  
334              compareJsons(expected, actual);
335          }
336  
337          TEST_METHOD (SettingsSetOverviewLink)
338          {
339              const auto value = L"overview link";
340              Settings settings(nullptr, m_moduleName);
341              settings.set_overview_link(value);
342  
343              const auto expected = m_defaultSettingsJson;
344              expected.SetNamedValue(L"overview_link", json::JsonValue::CreateStringValue(value));
345              const auto actual = json::JsonObject::Parse(settings.serialize());
346  
347              compareJsons(expected, actual);
348          }
349  
350          TEST_METHOD (SettingsSetVideoLink)
351          {
352              const auto value = L"video link";
353              Settings settings(nullptr, m_moduleName);
354              settings.set_video_link(value);
355  
356              const auto expected = m_defaultSettingsJson;
357              expected.SetNamedValue(L"video_link", json::JsonValue::CreateStringValue(value));
358              const auto actual = json::JsonObject::Parse(settings.serialize());
359  
360              compareJsons(expected, actual);
361          }
362  
363          TEST_METHOD (SettingsAddBoolTogglePositive)
364          {
365              const auto value = true;
366  
367              Settings settings(nullptr, m_moduleName);
368              settings.add_bool_toggle(m_defaultSettingsName, m_defaultSettingsDescription, value);
369  
370              auto expected = m_defaultSettingsJson;
371              auto expectedProperties = createSettingsProperties(L"bool_toggle");
372              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateBooleanValue(value));
373              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
374  
375              const auto actual = json::JsonObject::Parse(settings.serialize());
376  
377              compareJsons(expected, actual);
378          }
379  
380          TEST_METHOD (SettingsAddBoolToggleNegative)
381          {
382              const auto value = false;
383  
384              Settings settings(nullptr, m_moduleName);
385              settings.add_bool_toggle(m_defaultSettingsName, m_defaultSettingsDescription, value);
386  
387              auto expected = m_defaultSettingsJson;
388              auto expectedProperties = createSettingsProperties(L"bool_toggle");
389              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateBooleanValue(value));
390              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
391  
392              const auto actual = json::JsonObject::Parse(settings.serialize());
393  
394              compareJsons(expected, actual);
395          }
396  
397          TEST_METHOD (SettingsAddSpinner)
398          {
399              const int value = 738543;
400              const int min = 0;
401              const int max = 1000000;
402              const int step = 10;
403  
404              Settings settings(nullptr, m_moduleName);
405              settings.add_int_spinner(m_defaultSettingsName, m_defaultSettingsDescription, value, min, max, step);
406  
407              auto expected = m_defaultSettingsJson;
408              auto expectedProperties = createSettingsProperties(L"int_spinner");
409              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateNumberValue(value));
410              expectedProperties.SetNamedValue(L"min", json::JsonValue::CreateNumberValue(min));
411              expectedProperties.SetNamedValue(L"max", json::JsonValue::CreateNumberValue(max));
412              expectedProperties.SetNamedValue(L"step", json::JsonValue::CreateNumberValue(step));
413              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
414  
415              const auto actual = json::JsonObject::Parse(settings.serialize());
416  
417              compareJsons(expected, actual);
418          }
419  
420          TEST_METHOD (SettingsAddString)
421          {
422              const auto value = L"string text ";
423  
424              Settings settings(nullptr, m_moduleName);
425              settings.add_string(m_defaultSettingsName, m_defaultSettingsDescription, value);
426  
427              auto expected = m_defaultSettingsJson;
428              auto expectedProperties = createSettingsProperties(L"string_text");
429              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
430              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
431  
432              const auto actual = json::JsonObject::Parse(settings.serialize());
433  
434              compareJsons(expected, actual);
435          }
436  
437          TEST_METHOD(SettingsAddLargeHeader)
438          {
439              const auto value = L"large header sample text ";
440  
441              Settings settings(nullptr, m_moduleName);
442              settings.add_header_szLarge(m_defaultSettingsName, m_defaultSettingsDescription, value);
443  
444              auto expected = m_defaultSettingsJson;
445              auto expectedProperties = createSettingsProperties(L"header_large");
446              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
447              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
448  
449              const auto actual = json::JsonObject::Parse(settings.serialize());
450  
451              compareJsons(expected, actual);
452          }
453          
454          TEST_METHOD(SettingsAddStringMultiline)
455          {
456              const auto value = L"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident,\nsunt in culpa qui officia deserunt mollit anim id est laborum.";
457  
458              Settings settings(nullptr, m_moduleName);
459              settings.add_multiline_string(m_defaultSettingsName, m_defaultSettingsDescription, value);
460  
461              auto expected = m_defaultSettingsJson;
462              auto expectedProperties = createSettingsProperties(L"string_text");
463              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
464              expectedProperties.SetNamedValue(L"multiline", json::JsonValue::CreateBooleanValue(true));
465              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
466  
467              const auto actual = json::JsonObject::Parse(settings.serialize());
468  
469              compareJsons(expected, actual);
470          }
471  
472          TEST_METHOD (SettingsAddColorPicker)
473          {
474              const auto value = L"#ffffff";
475  
476              Settings settings(nullptr, m_moduleName);
477              settings.add_color_picker(m_defaultSettingsName, m_defaultSettingsDescription, value);
478  
479              auto expected = m_defaultSettingsJson;
480              auto expectedProperties = createSettingsProperties(L"color_picker");
481              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
482              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
483  
484              const auto actual = json::JsonObject::Parse(settings.serialize());
485  
486              compareJsons(expected, actual);
487          }
488  
489          TEST_METHOD (SettingsAddHotkey)
490          {
491              const auto value = PowerToysSettings::HotkeyObject::from_settings(true, true, true, true, 0);
492  
493              Settings settings(nullptr, m_moduleName);
494              settings.add_hotkey(m_defaultSettingsName, m_defaultSettingsDescription, value);
495  
496              auto expected = m_defaultSettingsJson;
497              auto expectedProperties = createSettingsProperties(L"hotkey");
498              expectedProperties.SetNamedValue(L"value", value.get_json());
499              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
500  
501              const auto actual = json::JsonObject::Parse(settings.serialize());
502  
503              compareJsons(expected, actual);
504          }
505  
506          TEST_METHOD (SettingsAddChoiceGroup)
507          {
508              const auto value = L"choice group value";
509              const auto keysAndTexts = {
510                  std::make_pair<std::wstring, std::wstring>(L"key1", L"value1"),
511                  std::make_pair<std::wstring, std::wstring>(L"key2", L"value2"),
512                  std::make_pair<std::wstring, std::wstring>(L"key3", L"value3")
513              };
514  
515              Settings settings(nullptr, m_moduleName);
516              settings.add_choice_group(m_defaultSettingsName, m_defaultSettingsDescription, value, keysAndTexts);
517  
518              auto expected = m_defaultSettingsJson;
519              auto expectedProperties = createSettingsProperties(L"choice_group");
520              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
521              json::JsonArray options;
522              for (const auto& [key, text] : keysAndTexts)
523              {
524                  json::JsonObject entry;
525                  entry.SetNamedValue(L"key", json::value(key));
526                  entry.SetNamedValue(L"text", json::value(text));
527                  options.Append(std::move(entry));
528              }
529              expectedProperties.SetNamedValue(L"options", std::move(options));
530              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
531  
532              const auto actual = json::JsonObject::Parse(settings.serialize());
533  
534              compareJsons(expected, actual);
535          }
536  
537          TEST_METHOD (SettingsAddChoiceGroupEmpty)
538          {
539              const auto value = L"choice group value";
540  
541              Settings settings(nullptr, m_moduleName);
542              settings.add_choice_group(m_defaultSettingsName, m_defaultSettingsDescription, value, {});
543  
544              auto expected = m_defaultSettingsJson;
545              auto expectedProperties = createSettingsProperties(L"choice_group");
546              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
547              expectedProperties.SetNamedValue(L"options", json::JsonArray());
548              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
549  
550              const auto actual = json::JsonObject::Parse(settings.serialize());
551  
552              compareJsons(expected, actual);
553          }
554  
555          TEST_METHOD (SettingsAddDropdown)
556          {
557              const auto value = L"dropdown value";
558              const auto keysAndTexts = {
559                  std::make_pair<std::wstring, std::wstring>(L"key1", L"value1"),
560                  std::make_pair<std::wstring, std::wstring>(L"key2", L"value2"),
561                  std::make_pair<std::wstring, std::wstring>(L"key3", L"value3")
562              };
563  
564              Settings settings(nullptr, m_moduleName);
565              settings.add_dropdown(m_defaultSettingsName, m_defaultSettingsDescription, value, keysAndTexts);
566  
567              auto expected = m_defaultSettingsJson;
568              auto expectedProperties = createSettingsProperties(L"dropdown");
569              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
570              json::JsonArray options;
571              for (const auto& [key, text] : keysAndTexts)
572              {
573                  json::JsonObject entry;
574                  entry.SetNamedValue(L"key", json::value(key));
575                  entry.SetNamedValue(L"text", json::value(text));
576                  options.Append(std::move(entry));
577              }
578              expectedProperties.SetNamedValue(L"options", std::move(options));
579              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
580  
581              const auto actual = json::JsonObject::Parse(settings.serialize());
582  
583              compareJsons(expected, actual);
584          }
585  
586          TEST_METHOD (SettingsAddDropdownEmpty)
587          {
588              const auto value = L"dropdown value";
589  
590              Settings settings(nullptr, m_moduleName);
591              settings.add_dropdown(m_defaultSettingsName, m_defaultSettingsDescription, value, {});
592  
593              auto expected = m_defaultSettingsJson;
594              auto expectedProperties = createSettingsProperties(L"dropdown");
595              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
596              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
597  
598              const auto actual = json::JsonObject::Parse(settings.serialize());
599  
600              compareJsons(expected, actual);
601          }
602  
603          TEST_METHOD (SettingsAddCustomAction)
604          {
605              const auto value = L"custom action value";
606              const std::wstring buttonText = L"button text";
607  
608              Settings settings(nullptr, m_moduleName);
609              settings.add_custom_action(m_defaultSettingsName, m_defaultSettingsDescription, buttonText, value);
610  
611              auto expected = m_defaultSettingsJson;
612              auto expectedProperties = createSettingsProperties(L"custom_action");
613              expectedProperties.SetNamedValue(L"value", json::JsonValue::CreateStringValue(value));
614              expectedProperties.SetNamedValue(L"button_text", json::JsonValue::CreateStringValue(buttonText));
615              expected.GetNamedObject(L"properties").SetNamedValue(m_defaultSettingsName, expectedProperties);
616  
617              const auto actual = json::JsonObject::Parse(settings.serialize());
618  
619              compareJsons(expected, actual);
620          }
621      };
622  
623      TEST_CLASS (CustomActionObjectUnitTests)
624      {
625      public:
626          TEST_METHOD (CustomActionObjectName)
627          {
628              const std::wstring json = L"{\"action_name\": \"action name\", \"value\": \"action value\"}";
629              CustomActionObject obj = CustomActionObject::from_json_string(json);
630              Assert::AreEqual(std::wstring(L"action name"), obj.get_name());
631          }
632  
633          TEST_METHOD (CustomActionObjectValue)
634          {
635              const std::wstring json = L"{\"action_name\": \"action name\", \"value\": \"action value\"}";
636              CustomActionObject obj = CustomActionObject::from_json_string(json);
637              Assert::AreEqual(std::wstring(L"action value"), obj.get_value());
638          }
639      };
640  
641      TEST_CLASS (HotkeyObjectUnitTests)
642      {
643      private:
644          json::JsonObject m_defaultHotkeyJson = json::JsonObject::Parse(L"{\"key\":\"(Key 0)\", \"code\": 123, \"win\": true, \"ctrl\": true, \"alt\": true, \"shift\": true}");
645          json::JsonObject m_defaultHotkeyJsonAlternative = json::JsonObject::Parse(L"{\"key\":\"(Key 0)\", \"code\": 123, \"win\": false, \"ctrl\": false, \"alt\": false, \"shift\": false}");
646  
647      public:
648          TEST_METHOD (GetKeyFromJson)
649          {
650              HotkeyObject object = HotkeyObject::from_json(m_defaultHotkeyJson);
651              Assert::AreEqual(std::wstring(L"(Key 0)"), object.get_key());
652          }
653  
654          TEST_METHOD (GetKeyFromJsonString)
655          {
656              HotkeyObject object = HotkeyObject::from_json_string(m_defaultHotkeyJson.Stringify());
657              Assert::AreEqual(std::wstring(L"(Key 0)"), object.get_key());
658          }
659  
660          TEST_METHOD (GetCodeFromJson)
661          {
662              HotkeyObject object = HotkeyObject::from_json(m_defaultHotkeyJson);
663              Assert::AreEqual(123U, object.get_code());
664          }
665  
666          TEST_METHOD (GetCodeFromJsonString)
667          {
668              HotkeyObject object = HotkeyObject::from_json_string(m_defaultHotkeyJson.Stringify());
669              Assert::AreEqual(123U, object.get_code());
670          }
671  
672          TEST_METHOD (GetCodeFromSettings)
673          {
674              HotkeyObject object = HotkeyObject::from_settings(true, true, true, true, 123);
675              Assert::AreEqual(123U, object.get_code());
676          }
677  
678          TEST_METHOD (GetWinPressedFromJson)
679          {
680              HotkeyObject object = HotkeyObject::from_json(m_defaultHotkeyJson);
681              Assert::AreEqual(true, object.win_pressed());
682  
683              HotkeyObject objectNegativeValues = HotkeyObject::from_json(m_defaultHotkeyJsonAlternative);
684              Assert::AreEqual(false, objectNegativeValues.win_pressed());
685          }
686  
687          TEST_METHOD (GetWinPressedFromJsonString)
688          {
689              HotkeyObject object = HotkeyObject::from_json_string(m_defaultHotkeyJson.Stringify());
690              Assert::AreEqual(true, object.win_pressed());
691  
692              HotkeyObject objectNegativeValues = HotkeyObject::from_json_string(m_defaultHotkeyJsonAlternative.Stringify());
693              Assert::AreEqual(false, objectNegativeValues.win_pressed());
694          }
695  
696          TEST_METHOD (GetWinPressedFromSettings)
697          {
698              HotkeyObject object = HotkeyObject::from_settings(true, true, true, true, 123);
699              Assert::AreEqual(true, object.win_pressed());
700  
701              HotkeyObject objectNegativeValues = HotkeyObject::from_settings(false, true, true, true, 123);
702              Assert::AreEqual(false, objectNegativeValues.win_pressed());
703          }
704  
705          TEST_METHOD (GetCtrlPressedFromJson)
706          {
707              HotkeyObject object = HotkeyObject::from_json(m_defaultHotkeyJson);
708              Assert::AreEqual(true, object.ctrl_pressed());
709  
710              HotkeyObject objectNegativeValues = HotkeyObject::from_json(m_defaultHotkeyJsonAlternative);
711              Assert::AreEqual(false, objectNegativeValues.ctrl_pressed());
712          }
713  
714          TEST_METHOD (GetCtrlPressedFromJsonString)
715          {
716              HotkeyObject object = HotkeyObject::from_json_string(m_defaultHotkeyJson.Stringify());
717              Assert::AreEqual(true, object.ctrl_pressed());
718  
719              HotkeyObject objectNegativeValues = HotkeyObject::from_json_string(m_defaultHotkeyJsonAlternative.Stringify());
720              Assert::AreEqual(false, objectNegativeValues.ctrl_pressed());
721          }
722  
723          TEST_METHOD (GetCtrlPressedFromSettings)
724          {
725              HotkeyObject object = HotkeyObject::from_settings(true, true, true, true, 123);
726              Assert::AreEqual(true, object.ctrl_pressed());
727  
728              HotkeyObject objectNegativeValues = HotkeyObject::from_settings(true, false, true, true, 123);
729              Assert::AreEqual(false, objectNegativeValues.ctrl_pressed());
730          }
731  
732          TEST_METHOD (GetAltPressedFromJson)
733          {
734              HotkeyObject object = HotkeyObject::from_json(m_defaultHotkeyJson);
735              Assert::AreEqual(true, object.alt_pressed());
736  
737              HotkeyObject objectNegativeValues = HotkeyObject::from_json(m_defaultHotkeyJsonAlternative);
738              Assert::AreEqual(false, objectNegativeValues.alt_pressed());
739          }
740  
741          TEST_METHOD (GetAltPressedFromJsonString)
742          {
743              HotkeyObject object = HotkeyObject::from_json_string(m_defaultHotkeyJson.Stringify());
744              Assert::AreEqual(true, object.alt_pressed());
745  
746              HotkeyObject objectNegativeValues = HotkeyObject::from_json_string(m_defaultHotkeyJsonAlternative.Stringify());
747              Assert::AreEqual(false, objectNegativeValues.alt_pressed());
748          }
749  
750          TEST_METHOD (GetAltPressedFromSettings)
751          {
752              HotkeyObject object = HotkeyObject::from_settings(true, true, true, true, 123);
753              Assert::AreEqual(true, object.alt_pressed());
754  
755              HotkeyObject objectNegativeValues = HotkeyObject::from_settings(true, true, false, true, 123);
756              Assert::AreEqual(false, objectNegativeValues.alt_pressed());
757          }
758  
759          TEST_METHOD (GetShiftPressedFromJson)
760          {
761              HotkeyObject object = HotkeyObject::from_json(m_defaultHotkeyJson);
762              Assert::AreEqual(true, object.shift_pressed());
763  
764              HotkeyObject objectNegativeValues = HotkeyObject::from_json(m_defaultHotkeyJsonAlternative);
765              Assert::AreEqual(false, objectNegativeValues.shift_pressed());
766          }
767  
768          TEST_METHOD (GetShiftPressedFromJsonString)
769          {
770              HotkeyObject object = HotkeyObject::from_json_string(m_defaultHotkeyJson.Stringify());
771              Assert::AreEqual(true, object.shift_pressed());
772  
773              HotkeyObject objectNegativeValues = HotkeyObject::from_json_string(m_defaultHotkeyJsonAlternative.Stringify());
774              Assert::AreEqual(false, objectNegativeValues.shift_pressed());
775          }
776  
777          TEST_METHOD (GetShiftPressedFromSettings)
778          {
779              HotkeyObject object = HotkeyObject::from_settings(true, true, true, true, 123);
780              Assert::AreEqual(true, object.shift_pressed());
781  
782              HotkeyObject objectNegativeValues = HotkeyObject::from_settings(true, true, true, false, 123);
783              Assert::AreEqual(false, objectNegativeValues.shift_pressed());
784          }
785  
786          TEST_METHOD (GetModifiersRepeat)
787          {
788              std::map<UINT, HotkeyObject> expectedMap = {
789                  std::make_pair(0x0000, HotkeyObject::from_settings(false, false, false, false, 0)),
790                  std::make_pair(0x0001, HotkeyObject::from_settings(false, false, true, false, 0)),
791                  std::make_pair(0x0002, HotkeyObject::from_settings(false, true, false, false, 0)),
792                  std::make_pair(0x0003, HotkeyObject::from_settings(false, true, true, false, 0)),
793                  std::make_pair(0x0004, HotkeyObject::from_settings(false, false, false, true, 0)),
794                  std::make_pair(0x0005, HotkeyObject::from_settings(false, false, true, true, 0)),
795                  std::make_pair(0x0006, HotkeyObject::from_settings(false, true, false, true, 0)),
796                  std::make_pair(0x0007, HotkeyObject::from_settings(false, true, true, true, 0)),
797                  std::make_pair(0x0008, HotkeyObject::from_settings(true, false, false, false, 0)),
798                  std::make_pair(0x0009, HotkeyObject::from_settings(true, false, true, false, 0)),
799                  std::make_pair(0x000A, HotkeyObject::from_settings(true, true, false, false, 0)),
800                  std::make_pair(0x000B, HotkeyObject::from_settings(true, true, true, false, 0)),
801                  std::make_pair(0x000C, HotkeyObject::from_settings(true, false, false, true, 0)),
802                  std::make_pair(0x000D, HotkeyObject::from_settings(true, false, true, true, 0)),
803                  std::make_pair(0x000E, HotkeyObject::from_settings(true, true, false, true, 0)),
804                  std::make_pair(0x000F, HotkeyObject::from_settings(true, true, true, true, 0))
805              };
806  
807              for (const auto& iter : expectedMap)
808              {
809                  Assert::AreEqual(iter.first, iter.second.get_modifiers_repeat());
810              }
811          }
812  
813          TEST_METHOD (GetModifiers)
814          {
815              std::map<UINT, HotkeyObject> expectedMap = {
816                  std::make_pair(0x4000, HotkeyObject::from_settings(false, false, false, false, 0)),
817                  std::make_pair(0x4001, HotkeyObject::from_settings(false, false, true, false, 0)),
818                  std::make_pair(0x4002, HotkeyObject::from_settings(false, true, false, false, 0)),
819                  std::make_pair(0x4003, HotkeyObject::from_settings(false, true, true, false, 0)),
820                  std::make_pair(0x4004, HotkeyObject::from_settings(false, false, false, true, 0)),
821                  std::make_pair(0x4005, HotkeyObject::from_settings(false, false, true, true, 0)),
822                  std::make_pair(0x4006, HotkeyObject::from_settings(false, true, false, true, 0)),
823                  std::make_pair(0x4007, HotkeyObject::from_settings(false, true, true, true, 0)),
824                  std::make_pair(0x4008, HotkeyObject::from_settings(true, false, false, false, 0)),
825                  std::make_pair(0x4009, HotkeyObject::from_settings(true, false, true, false, 0)),
826                  std::make_pair(0x400A, HotkeyObject::from_settings(true, true, false, false, 0)),
827                  std::make_pair(0x400B, HotkeyObject::from_settings(true, true, true, false, 0)),
828                  std::make_pair(0x400C, HotkeyObject::from_settings(true, false, false, true, 0)),
829                  std::make_pair(0x400D, HotkeyObject::from_settings(true, false, true, true, 0)),
830                  std::make_pair(0x400E, HotkeyObject::from_settings(true, true, false, true, 0)),
831                  std::make_pair(0x400F, HotkeyObject::from_settings(true, true, true, true, 0))
832              };
833  
834              for (const auto& iter : expectedMap)
835              {
836                  Assert::AreEqual(iter.first, iter.second.get_modifiers());
837              }
838          }
839      };
840  }