// Copyright (c) 2022 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "source/diff/lcs.h" #include #include "gtest/gtest.h" namespace spvtools { namespace diff { namespace { using Sequence = std::vector; using LCS = LongestCommonSubsequence; void VerifyMatch(const Sequence& src, const Sequence& dst, size_t expected_match_count) { DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); EXPECT_EQ(match_count, expected_match_count); size_t src_cur = 0; size_t dst_cur = 0; size_t matches_seen = 0; while (src_cur < src.size() && dst_cur < dst.size()) { if (src_match[src_cur] && dst_match[dst_cur]) { EXPECT_EQ(src[src_cur], dst[dst_cur]) << "Src: " << src_cur << " Dst: " << dst_cur; ++src_cur; ++dst_cur; ++matches_seen; continue; } if (!src_match[src_cur]) { ++src_cur; } if (!dst_match[dst_cur]) { ++dst_cur; } } EXPECT_EQ(matches_seen, expected_match_count); } TEST(LCSTest, EmptySequences) { Sequence src, dst; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); EXPECT_EQ(match_count, 0u); EXPECT_TRUE(src_match.empty()); EXPECT_TRUE(dst_match.empty()); } TEST(LCSTest, EmptySrc) { Sequence src, dst = {1, 2, 3}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); EXPECT_EQ(match_count, 0u); EXPECT_TRUE(src_match.empty()); EXPECT_EQ(dst_match, DiffMatch(3, false)); } TEST(LCSTest, EmptyDst) { Sequence src = {1, 2, 3}, dst; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); EXPECT_EQ(match_count, 0u); EXPECT_EQ(src_match, DiffMatch(3, false)); EXPECT_TRUE(dst_match.empty()); } TEST(LCSTest, Identical) { Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); EXPECT_EQ(match_count, 6u); EXPECT_EQ(src_match, DiffMatch(6, true)); EXPECT_EQ(dst_match, DiffMatch(6, true)); } TEST(LCSTest, SrcPrefix) { Sequence src = {1, 2, 3, 4}, dst = {1, 2, 3, 4, 5, 6}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); const DiffMatch src_expect = {true, true, true, true}; const DiffMatch dst_expect = {true, true, true, true, false, false}; EXPECT_EQ(match_count, 4u); EXPECT_EQ(src_match, src_expect); EXPECT_EQ(dst_match, dst_expect); } TEST(LCSTest, DstPrefix) { Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); const DiffMatch src_expect = {true, true, true, true, true, false}; const DiffMatch dst_expect = {true, true, true, true, true}; EXPECT_EQ(match_count, 5u); EXPECT_EQ(src_match, src_expect); EXPECT_EQ(dst_match, dst_expect); } TEST(LCSTest, SrcSuffix) { Sequence src = {3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); const DiffMatch src_expect = {true, true, true, true}; const DiffMatch dst_expect = {false, false, true, true, true, true}; EXPECT_EQ(match_count, 4u); EXPECT_EQ(src_match, src_expect); EXPECT_EQ(dst_match, dst_expect); } TEST(LCSTest, DstSuffix) { Sequence src = {1, 2, 3, 4, 5, 6}, dst = {5, 6}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); const DiffMatch src_expect = {false, false, false, false, true, true}; const DiffMatch dst_expect = {true, true}; EXPECT_EQ(match_count, 2u); EXPECT_EQ(src_match, src_expect); EXPECT_EQ(dst_match, dst_expect); } TEST(LCSTest, None) { Sequence src = {1, 3, 5, 7, 9}, dst = {2, 4, 6, 8, 10, 12}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); EXPECT_EQ(match_count, 0u); EXPECT_EQ(src_match, DiffMatch(5, false)); EXPECT_EQ(dst_match, DiffMatch(6, false)); } TEST(LCSTest, NonContiguous) { Sequence src = {1, 2, 3, 4, 5, 6, 10}, dst = {2, 4, 5, 8, 9, 10, 12}; DiffMatch src_match, dst_match; LCS lcs(src, dst); size_t match_count = lcs.Get([](int s, int d) { return s == d; }, &src_match, &dst_match); const DiffMatch src_expect = {false, true, false, true, true, false, true}; const DiffMatch dst_expect = {true, true, true, false, false, true, false}; EXPECT_EQ(match_count, 4u); EXPECT_EQ(src_match, src_expect); EXPECT_EQ(dst_match, dst_expect); } TEST(LCSTest, WithDuplicates) { Sequence src = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, dst = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4}; VerifyMatch(src, dst, 6); } TEST(LCSTest, Large) { const std::string src_str = "GUJwrJSlkKJXxCVIAxlVgnUyOrdyRyFtlZwWMmFhYGfkFTNnhiBmClgHyrcXMVwfrRxNUfQk" "qaoGvCbPZHAzXsaZpXHPfJxOMCUtRDmIQpfiXKbHQbhTfPqhxBDWvmTQAqwsWTLajZYtMUnf" "hNNCfkuAXkZsaebwEbIZOxTDZsqSMUfCMoGeKJGVSNFgLTiBMbdvchHGfFRkHKcYCDjBfIcj" "todPnvDjzQYWBcvIfVvyBzHikrwpDORaGEZLhmyztIFCLJOqeLhOzERYmVqzlsoUzruTXTXq" "DLTxQRakOCMRrgRzCDTXfwwfDcKMBVnxRZemjcwcEsOVxwtwdBCWJycsDcZKlvrCvZaenKlv" "vyByDQeLdxAyBnPkIMQlMQwqjUfRLybeoaOanlbFkpTPPZdHelQrIvucTHzMpWWQTbuANwvN" "OVhCGGoIcGNDpfIsaBexlMMdHsxMGerTngmjpdPeQQJHfvKZkdYqAzrtDohqtDsaFMxQViVQ" "YszDVgyoSHZdOXAvXkJidojLvGZOzhRajVPhWDwKuGqdaympELxHsrXAJYufdCPwJdGJfWqq" "yvTWpcrFHOIuCEmNLnSCDsxQGRVDwyCykBJazhApfCnrOadnafvqfVuFqEXMSrYbHTfTnzbz" "MhISyOtMUITaurCXvanCbuOXBhHyCjOhVbxnMvhlPmZBMQgEHCghtAJVMXGPNRtszVZlPxVl" "QIPTBnPUPejlyZGPqeICyNngdQGkvKbIoWlTLBtVhMdBeUMozNlKQTIPYBeImVcMdLafuxUf" "TIXysmcTrUTcspOSKBxhdhLwiRnREGFWJTfUKsgGOAQeYojXdrqsGjMJfiKalyoiqrgLnlij" "CtOapoxDGVOOBalNYGzCtBlxbvaAzxipGnJpOEbmXcpeoIsAdxspKBzBDgoPVxnuRBUwmTSr" "CRpWhxikgUYQVCwalLUIeBPRyhhsECGCXJmGDZSCIUaBwROkigzdeVPOXhgCGBEprWtNdYfL" "tOUYJHQXxiIJgSGmWntezJFpNQoTPbRRYAGhtYvAechvBcYWocLkYFxsDAuszvQNLXdhmAHw" "DErcjbtCdQllnKcDADVNWVezljjLrAuyGHetINMgAvJZwOEYakihYVUbZGCsHEufluLNyNHy" "gqtSTSFFjBHiIqQejTPWybLdpWNwZrWvIWnlzUcGNQPEYHVPCbteWknjAnWrdTBeCbHUDBoK" "aHvDStmpNRGIjvlumiZTbdZNAzUeSFnFChCsSExwXeEfDJfjyOoSBofHzJqJErvHLNyUJTjX" "qmtgKPpMKohUPBMhtCteQFcNEpWrUVGbibMOpvBwdiWYXNissArpSasVJFgDzrqTyGkerTMX" "gcrzFUGFZRhNdekaJeKYPogsofJaRsUQmIRyYdkrxKeMgLPpwOfSKJOqzXDoeHljTzhOwEVy" "krOEnACFrWhufajsMitjOWdLOHHchQDddGPzxknEgdwmZepKDvRZGCuPqzeQkjOPqUBKpKLJ" "eKieSsRXkaqxSPGajfvPKmwFWdLByEcLgvrmteazgFjmMGrLYqRRxzUOfOCokenqHVYstBHf" "AwsWsqPTvqsRJUfGGTaYiylZMGbQqTzINhFHvdlRQvvYKBcuAHdBeKlHSxVrSsEKbcAvnIcf" "xzdVDdwQPHMCHeZZRpGHWvKzgTGzSTbYTeOPyKvvYWmQToTpsjAtKUJUjcEHWhmdBLDTBMHJ" "ivBXcLGtCsumNNVFyGbVviGmqHTdyBlkneibXBesKJGOUzOtIwXCPJggqBekSzNQYkALlItk" "cbEhbdXAIKVHYpInLwxXalKZrkrpxtfuagqMGmRJnJbFQaEoYMoqPsxZpocddPXXPyvxVkaF" "qdKISejWDhBImnEEOPDcyWTubbfVfwUztciaFJcsPLhgYVfhqlOfoNjKbmTFptFttYuyBrUI" "zzmZypOqrjQHTGFwlHStpIwxPtMvtsEDpsmWIgwzYgwmdpbMOnfElZMYpVIcvzSWejeJcdUB" "QUoBRUmGQVVWvEDseuozrDjgdXFScPwwsgaUPwSzScfBNrkpmEFDSZLKfNjMqvOmUtocUkbo" "VGFEKgGLbNruwLgXHTloWDrnqymPVAtzjWPutonIsMDPeeCmTjYWAFXcyTAlBeiJTIRkZxiM" "kLjMnAflSNJzmZkatXkYiPEMYSmzHbLKEizHbEjQOxBDzpRHiFjhedqiyMiUMvThjaRFmwll" "aMGgwKBIKepwyoEdnuhtzJzboiNEAFKiqiWxxmkRFRoTiFWXLPAWLuzSCrajgkQhDxAQDqyM" "VwZlhZicQLEDYYisEalesDWZAYzcvENuHUwRutIsGgsdoYwOZiURhcgdbTGWBNqhrFjvTQCj" "VlTPNlRdRLaaqzUBBwbdtyXFkCBUYYMbmRrkFxfxbCqkgZNGyHPKLkOPnezfVTRmRQgCgHbx" "wcZlInVOwmFePnSIbThMJosimzkhfuiqYEpwHQiemqsSDNNdbNhBLzbsPZBJZujSHJGtYKGb" "HaAYGJZxBumsKUrATwPuqXFLfwNyImLQbchBKiJAYRZhkcrKCHXBEGYyBhBGvSqvabcRUrfq" "AbPiMzjHAehGYjDEmxAnYLyoSFdeWVrfJUCuYZPluhXEBuyUpKaRXDKXeiCvGidpvATwMbcz" "DZpzxrhTZYyrFORFQWTbPLCBjMKMhlRMFEiarDgGPttjmkrQVlujztMSkxXffXFNqLWOLThI" "KBoyMHoFTEPCdUAZjLTifAdjjUehyDLEGKlRTFoLpjalziRSUjZfRYbNzhiHgTHowMMkKTwE" "ZgnqiirMtnNpaBJqhcIVrWXPpcPWZfRpsPstHleFJDZYAsxYhOREVbFtebXTZRAIjGgWeoiN" "qPLCCAVadqmUrjOcqIbdCTpcDRWuDVbHrZOQRPhqbyvOWwxAWJphjLiDgoAybcjzgfVktPlj" "kNBCjelpuQfnYsiTgPpCNKYtOrxGaLEEtAuLdGdDsONHNhSn"; const std::string dst_str = "KzitfifORCbGhfNEbnbObUdFLLaAsLOpMkOeKupjCoatzqfHBkNJfSgqSMYouswfNMnoQngK" "jWwyPKmEnoZWyPBUdQRmKUNudUclueKXKQefUdXWUyyqtumzsFKznrLVLwfvPZpLChNYrrHK" "AtpfOuVHiUKyeRCrktJAhkyFKmPWrASEMvBLNOzuGlvinZjvZUUXazNEkyMPiOLdqXvCIroC" "MeWsvjHShlLhDwLZrVlpYBnDJmILcsNFDSoaLWOKNNkNGBgNBvVjPCJXAuKfsrKZhYcdEpxK" "UihiRkYvMiLyOUvaqBMklLDwEhvQBfCXHSRoqsLsSCzLZQhIYMhBapvHaPbDoRrHoJXZsNXc" "rxZYCrOMIzYcVPwDCFiHBFnPNTTeAeKEMGeVUeCaAeuWZmngyPWlQBcgWumSUIfbhjVYdnpV" "hRSJXrIoFZubBXfNOMhilAkVPixrhILZKgDoFTvytPFPfBLMnbhSOBmLWCbJsLQxrCrMAlOw" "RmfSQyGhrjhzYVqFSBHeoQBagFwyxIjcHFZngntpVHbSwqhwHeMnWSsISPljTxSNXfCxLebW" "GhMdlphtJbdvhEcjNpwPCFqhdquxCyOxkjsDUPNgjpDcpIMhMwMclNhfESTrroJaoyeGQclV" "gonnhuQRmXcBwcsWeLqjNngZOlyMyfeQBwnwMVJEvGqknDyzSApniRTPgJpFoDkJJhXQFuFB" "VqhuEPMRGCeTDOSEFmXeIHOnDxaJacvnmORwVpmrRhGjDpUCkuODNPdZMdupYExDEDnDLdNF" "iObKBaVWpGVMKdgNLgsNxcpypBPPKKoaajeSGPZQJWSOKrkLjiFexYVmUGxJnbTNsCXXLfZp" "jfxQAEVYvqKehBzMsVHVGWmTshWFAoCNDkNppzzjHBZWckrzSTANICioCJSpLwPwQvtXVxst" "nTRBAboPFREEUFazibpFesCsjzUOnECwoPCOFiwGORlIZVLpUkJyhYXCENmzTBLVigOFuCWO" "IiXBYmiMtsxnUdoqSTTGyEFFrQsNAjcDdOKDtHwlANWoUVwiJCMCQFILdGqzEePuSXFbOEOz" "dLlEnTJbKRSTfAFToOZNtDXTfFgvQiefAKbSUWUXFcpCjRYCBNXCCcLMjjuUDXErpiNsRuIx" "mgHsrObTEXcnmjdqxTGhTjTeYizNnkrJRhNQIqDXmZMwArBccnixpcuiGOOexjgkpcEyGAnz" "UbgiBfflTUyJfZeFFLrZVueFkSRosebnnwAnakIrywTGByhQKWvmNQJsWQezqLhHQzXnEpeD" "rFRTSQSpVxPzSeEzfWYzfpcenxsUyzOMLxhNEhfcuprDtqubsXehuqKqZlLQeSclvoGjuKJK" "XoWrazsgjXXnkWHdqFESZdMGDYldyYdbpSZcgBPgEKLWZHfBirNPLUadmajYkiEzmGuWGELB" "WLiSrMdaGSbptKmgYVqMGcQaaATStiZYteGAPxSEBHuAzzjlRHYsrdDkaGNXmzRGoalJMiCC" "GMtWSDMhgvRSEgKnywbRgnqWXFlwrhXbbvcgLGtWSuKQBiqIlWkfPMozOTWgVoLHavDJGRYI" "YerrmZnTMtuuxmZALWakfzUbksTwoetqkOiRPGqGZepcVXHoZyOaaaijjZWQLlIhYwiQNbfc" "KCwhhFaMQBoaCnOecJEdKzdsMPFEYQuJNPYiiNtsYxaWBRuWjlLqGokHMNtyTQfSJKbgGdol" "fWlOZdupouQMfUWXIYHzyJHefMDnqxxasDxtgArvDqtwjDBaVEMACPkLFpiDOoKCHqkWVizh" "lKqbOHpsPKkhjRQRNGYRYEfxtBjYvlCvHBNUwVuIwDJYMqHxEFtwdLqYWvjdOfQmNiviDfUq" "pbucbNwjNQfMYgwUuPnQWIPOlqHcbjtuDXvTzLtkdBQanJbrmLSyFqSapZCSPMDOrxWVYzyO" "lwDTTJFmKxoyfPunadkHcrcSQaQsAbrQtbhqwSTXGTPURYTCbNozjAVwbmcyVxIbZudBZWYm" "rnSDyelGCRRWYtrUxvOVWlTLHHdYuAmVMGnGbHscbjmjmAzmYLaCxNNwhmMYdExKvySxuYpE" "rVGwfqMngBCHnZodotNaNJZiNRFWubuPDfiywXPiyVWoQMeOlSuWmpilLTIFOvfpjmJTgrWa" "dgoxYeyPyOaglOvZVGdFOBSeqEcGXBwjoeUAXqkpvOxEpSXhmklKZydTvRVYVvfQdRNNDkCT" "dLNfcZCFQbZORdcDOhwotoyccrSbWvlqYMoiAYeEpDzZTvkamapzZMmCpEutZFCcHBWGIIkr" "urwDNHrobaErPpclyEegLJDtkfUWSNWZosWSbBGAHIvJsFNUlJXbnkSVycLkOVQVcNcUtiBy" "djLDIFsycbPBEWaMvCbntNtJlOeCttvXypGnHAQFnFSiXFWWqonWuVIKmVPpKXuJtFguXCWC" "rNExYYvxLGEmuZJLJDjHgjlQyOzeieCpizJxkrdqKCgomyEkvsyVYSsLeyLvOZQrrgEJgRFK" "CjYtoOfluNrLdRMTRkQXmAiMRFwloYECpXCReAMxOkNiwCtutsrqWoMHsrogRqPoUCueonvW" "MTwmkAkajfGJkhnQidwpwIMEttQkzIMOPvvyWZHpqkMHWlNTeSKibfRfwDyxveKENZhtlPwP" "dfAjwegjRcavtFnkkTNVYdCdCrgdUvzsIcqmUjwGmVvuuQvjVrWWIDBmAzQtiZPYvCOEWjce" "rWzeqVKeiYTJBOedmQCVidOgUIEjfRnbGvUbctYxfRybJkdmeAkLZQMRMGPOnsPbFswXAoCK" "IxWGwohoPpEJxslbqHFKSwknxTmrDCITRZWEDkGQeucPxHBdYkduwbYhKnoxCKhgjBFiFawC" "QtgTDldTQmlOsBiGLquMjuecAbrUJJvNtXbFNGjWxaZPimSRXUJWgRbydpsczOqSFIeEtuKA" "ZpRhmLtPdVNKdSDQZeeImUFmUwXApRTUNHItyvFyJtNtn"; Sequence src; Sequence dst; src.reserve(src_str.length()); dst.reserve(dst_str.length()); for (char c : src_str) { src.push_back(c); } for (char c : dst_str) { dst.push_back(c); } VerifyMatch(src, dst, 723); } } // namespace } // namespace diff } // namespace spvtools