@@ -846,3 +846,198 @@ TEST(INISettingsInterface, SetPathDirtyFlag)
846846 si.SetPath (" /tmp/new_path.ini" );
847847 EXPECT_TRUE (si.IsDirty ());
848848}
849+
850+ // ---- Ordered Save ----
851+
852+ TEST (INISettingsInterface, OrderedSaveEmptyOrder)
853+ {
854+ INISettingsInterface si;
855+ si.LoadFromString (" [B]\n b = 2\n\n [A]\n a = 1\n " );
856+
857+ const std::string without_order = si.SaveToString ();
858+ const std::string with_empty_order = si.SaveToString ({});
859+ EXPECT_EQ (without_order, with_empty_order);
860+ }
861+
862+ TEST (INISettingsInterface, OrderedSaveBasic)
863+ {
864+ INISettingsInterface si;
865+ si.LoadFromString (" [Alpha]\n a = 1\n\n [Beta]\n b = 2\n\n [Gamma]\n g = 3\n " );
866+
867+ // Request Gamma first, then Alpha.
868+ const char * const order[] = {" Gamma" , " Alpha" };
869+ const std::string output = si.SaveToString (order);
870+ EXPECT_EQ (output,
871+ " [Gamma]\n g = 3\n\n "
872+ " [Alpha]\n a = 1\n\n "
873+ " [Beta]\n b = 2\n " );
874+ }
875+
876+ TEST (INISettingsInterface, OrderedSaveAllSections)
877+ {
878+ INISettingsInterface si;
879+ si.LoadFromString (" [A]\n a = 1\n\n [B]\n b = 2\n\n [C]\n c = 3\n " );
880+
881+ // Reverse the natural alphabetical order.
882+ const char * const order[] = {" C" , " B" , " A" };
883+ const std::string output = si.SaveToString (order);
884+ EXPECT_EQ (output,
885+ " [C]\n c = 3\n\n "
886+ " [B]\n b = 2\n\n "
887+ " [A]\n a = 1\n " );
888+ }
889+
890+ TEST (INISettingsInterface, OrderedSaveNonExistentSections)
891+ {
892+ INISettingsInterface si;
893+ si.LoadFromString (" [A]\n a = 1\n\n [B]\n b = 2\n " );
894+
895+ // "Missing" doesn't exist; should be silently skipped.
896+ const char * const order[] = {" Missing" , " B" };
897+ const std::string output = si.SaveToString (order);
898+ EXPECT_EQ (output,
899+ " [B]\n b = 2\n\n "
900+ " [A]\n a = 1\n " );
901+ }
902+
903+ TEST (INISettingsInterface, OrderedSaveRemainingInAlphabeticalOrder)
904+ {
905+ INISettingsInterface si;
906+ si.LoadFromString (" [Delta]\n d = 4\n\n [Alpha]\n a = 1\n\n [Charlie]\n c = 3\n\n [Bravo]\n b = 2\n " );
907+
908+ // Only specify Charlie; rest should come after in alphabetical order.
909+ const char * const order[] = {" Charlie" };
910+ const std::string output = si.SaveToString (order);
911+ EXPECT_EQ (output,
912+ " [Charlie]\n c = 3\n\n "
913+ " [Alpha]\n a = 1\n\n "
914+ " [Bravo]\n b = 2\n\n "
915+ " [Delta]\n d = 4\n " );
916+ }
917+
918+ TEST (INISettingsInterface, OrderedSaveSingleSection)
919+ {
920+ INISettingsInterface si;
921+ si.LoadFromString (" [Only]\n key = val\n " );
922+
923+ const char * const order[] = {" Only" };
924+ const std::string output = si.SaveToString (order);
925+ EXPECT_EQ (output, " [Only]\n key = val\n " );
926+ }
927+
928+ TEST (INISettingsInterface, OrderedSaveEmptyINI)
929+ {
930+ INISettingsInterface si;
931+ si.LoadFromString (" " );
932+
933+ const char * const order[] = {" A" , " B" };
934+ const std::string output = si.SaveToString (order);
935+ EXPECT_TRUE (output.empty ());
936+ }
937+
938+ TEST (INISettingsInterface, OrderedSaveDuplicateOrderEntries)
939+ {
940+ INISettingsInterface si;
941+ si.LoadFromString (" [A]\n a = 1\n\n [B]\n b = 2\n " );
942+
943+ // "A" appears twice; section A should only be written once.
944+ const char * const order[] = {" A" , " B" , " A" };
945+ const std::string output = si.SaveToString (order);
946+ EXPECT_EQ (output,
947+ " [A]\n a = 1\n\n "
948+ " [B]\n b = 2\n " );
949+ }
950+
951+ TEST (INISettingsInterface, OrderedSaveContentPreserved)
952+ {
953+ INISettingsInterface si;
954+ si.LoadFromString (" [Z]\n "
955+ " plain = hello\n "
956+ " quoted = \" value ; with # chars\"\n\n "
957+ " [A]\n "
958+ " num = 42\n " );
959+
960+ const char * const order[] = {" Z" };
961+ const std::string output = si.SaveToString (order);
962+
963+ // Z comes first (as ordered), A follows. Quoting is preserved on save.
964+ EXPECT_EQ (output,
965+ " [Z]\n plain = hello\n quoted = \" value ; with # chars\"\n\n "
966+ " [A]\n num = 42\n " );
967+ }
968+
969+ TEST (INISettingsInterface, OrderedSavePrefixMatching)
970+ {
971+ INISettingsInterface si;
972+ si.LoadFromString (" [Other]\n o = 0\n\n "
973+ " [Pad]\n p = 1\n\n "
974+ " [Pad/1]\n p1 = 2\n\n "
975+ " [Pad/2]\n p2 = 3\n " );
976+
977+ // "Pad" should match "Pad" (exact) and "Pad/1", "Pad/2" (prefix with /).
978+ const char * const order[] = {" Pad" };
979+ const std::string output = si.SaveToString (order);
980+ EXPECT_EQ (output,
981+ " [Pad]\n p = 1\n\n "
982+ " [Pad/1]\n p1 = 2\n\n "
983+ " [Pad/2]\n p2 = 3\n\n "
984+ " [Other]\n o = 0\n " );
985+ }
986+
987+ TEST (INISettingsInterface, OrderedSavePrefixBoundary)
988+ {
989+ INISettingsInterface si;
990+ si.LoadFromString (" [Pad]\n p = 1\n\n "
991+ " [Pad/1]\n p1 = 2\n\n "
992+ " [Pad2]\n p2 = 3\n\n "
993+ " [Padding]\n pd = 4\n " );
994+
995+ // "Pad" should match "Pad" and "Pad/1", but NOT "Pad2" or "Padding".
996+ const char * const order[] = {" Pad" };
997+ const std::string output = si.SaveToString (order);
998+ EXPECT_EQ (output,
999+ " [Pad]\n p = 1\n\n "
1000+ " [Pad/1]\n p1 = 2\n\n "
1001+ " [Pad2]\n p2 = 3\n\n "
1002+ " [Padding]\n pd = 4\n " );
1003+ }
1004+
1005+ TEST (INISettingsInterface, OrderedSavePrefixGroupsPreserveOrder)
1006+ {
1007+ INISettingsInterface si;
1008+ si.LoadFromString (" [Pad/3]\n p3 = 3\n\n "
1009+ " [Pad/1]\n p1 = 1\n\n "
1010+ " [Pad/2]\n p2 = 2\n\n "
1011+ " [Other]\n o = 0\n " );
1012+
1013+ // Sub-sections should appear in their natural (alphabetical) order within the prefix group.
1014+ const char * const order[] = {" Pad" };
1015+ const std::string output = si.SaveToString (order);
1016+ EXPECT_EQ (output,
1017+ " [Pad/1]\n p1 = 1\n\n "
1018+ " [Pad/2]\n p2 = 2\n\n "
1019+ " [Pad/3]\n p3 = 3\n\n "
1020+ " [Other]\n o = 0\n " );
1021+ }
1022+
1023+ TEST (INISettingsInterface, OrderedSaveMultiplePrefixes)
1024+ {
1025+ INISettingsInterface si;
1026+ si.LoadFromString (" [Hotkey]\n h = 0\n\n "
1027+ " [Hotkey/1]\n h1 = 1\n\n "
1028+ " [Other]\n o = 0\n\n "
1029+ " [Pad]\n p = 0\n\n "
1030+ " [Pad/1]\n p1 = 1\n\n "
1031+ " [Pad/2]\n p2 = 2\n " );
1032+
1033+ // Pad group first, then Hotkey group, then remaining.
1034+ const char * const order[] = {" Pad" , " Hotkey" };
1035+ const std::string output = si.SaveToString (order);
1036+ EXPECT_EQ (output,
1037+ " [Pad]\n p = 0\n\n "
1038+ " [Pad/1]\n p1 = 1\n\n "
1039+ " [Pad/2]\n p2 = 2\n\n "
1040+ " [Hotkey]\n h = 0\n\n "
1041+ " [Hotkey/1]\n h1 = 1\n\n "
1042+ " [Other]\n o = 0\n " );
1043+ }
0 commit comments