blob: e190933fcd988c2aa5511e3fc6130d81bcbec8f9 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/IDE/FuzzyStringMatcher.h"
#include "gtest/gtest.h"
using FuzzyStringMatcher = swift::ide::FuzzyStringMatcher;
TEST(FuzzyStringMatcher, BasicMatching) {
{
FuzzyStringMatcher m("ASDF");
EXPECT_TRUE(m.matchesCandidate("ASDF"));
EXPECT_TRUE(m.matchesCandidate("asdf"));
EXPECT_TRUE(m.matchesCandidate("xASDF"));
EXPECT_TRUE(m.matchesCandidate("ASDFX"));
EXPECT_TRUE(m.matchesCandidate("ASDFX"));
EXPECT_TRUE(m.matchesCandidate("a_s_d_f"));
EXPECT_FALSE(m.matchesCandidate("asd"));
EXPECT_FALSE(m.matchesCandidate(""));
}
{
FuzzyStringMatcher m("asDf");
EXPECT_TRUE(m.matchesCandidate("ASDF"));
EXPECT_TRUE(m.matchesCandidate("asdf"));
EXPECT_TRUE(m.matchesCandidate("xASDF"));
EXPECT_TRUE(m.matchesCandidate("ASDFX"));
EXPECT_TRUE(m.matchesCandidate("ASDFX"));
EXPECT_TRUE(m.matchesCandidate("a_s_d_f"));
EXPECT_FALSE(m.matchesCandidate("asd"));
EXPECT_FALSE(m.matchesCandidate(""));
}
}
TEST(FuzzyStringMatcher, SingleCharacterMatching) {
EXPECT_TRUE(FuzzyStringMatcher("A").matchesCandidate("a"));
EXPECT_TRUE(FuzzyStringMatcher("a").matchesCandidate("a"));
EXPECT_FALSE(FuzzyStringMatcher("a").matchesCandidate("b"));
EXPECT_FALSE(FuzzyStringMatcher("A").matchesCandidate("b"));
EXPECT_TRUE(FuzzyStringMatcher("a").matchesCandidate("bA"));
EXPECT_TRUE(FuzzyStringMatcher("A").matchesCandidate("bA"));
EXPECT_TRUE(FuzzyStringMatcher("A").matchesCandidate("ba"));
EXPECT_FALSE(FuzzyStringMatcher("a").matchesCandidate(""));
}
TEST(FuzzyStringMatcher, UnicodeMatching) {
// Single code point matching.
EXPECT_TRUE(FuzzyStringMatcher(u8"\u2602a\U0002000Bz")
.matchesCandidate(u8"\u2602A\U0002000BZ"));
// Same-order combining marks.
EXPECT_TRUE(FuzzyStringMatcher(u8"a\u0323\u0307")
.matchesCandidate(u8"A\u0323\u0307"));
// FIXME: Canonical equivalence. These should be the same.
EXPECT_FALSE(FuzzyStringMatcher(u8"a\u0307\u0323")
.matchesCandidate(u8"A\u0323\u0307"));
EXPECT_FALSE(FuzzyStringMatcher(u8"a\u00C5").matchesCandidate(u8"A\u030A"));
// FIXME: Compatibility equivalence. It would be good to make these the same
// too, since we're fuzzy matching.
EXPECT_FALSE(FuzzyStringMatcher(u8"fi").matchesCandidate(u8"\uFB01"));
EXPECT_FALSE(FuzzyStringMatcher(u8"25").matchesCandidate(u8"2\u2075"));
// FIXME: Case-insensitivity in non-ASCII characters.
EXPECT_FALSE(FuzzyStringMatcher(u8"\u00E0").matchesCandidate(u8"\u00C0"));
EXPECT_FALSE(FuzzyStringMatcher(u8"ss").matchesCandidate(u8"\u00DF"));
}
TEST(FuzzyStringMatcher, BasicScoring) {
FuzzyStringMatcher m("ASDF");
EXPECT_GT(m.scoreCandidate("ASDF"), m.scoreCandidate("ASDF_")); // exact
EXPECT_GT(m.scoreCandidate("ASDF"), m.scoreCandidate("asdf")); // case
EXPECT_GT(m.scoreCandidate("asdF"), m.scoreCandidate("asdf")); // case
EXPECT_GT(m.scoreCandidate("asdfz"), m.scoreCandidate("zasdf")); // earlier
}
TEST(FuzzyStringMatcher, BestMatchNotFirstMatch) {
{
FuzzyStringMatcher m("AS");
EXPECT_GT(m.scoreCandidate("abs_as"), m.scoreCandidate("abs_xx"));
}
{
FuzzyStringMatcher m("ASDF");
EXPECT_GT(m.scoreCandidate("absadef_asdf"),
m.scoreCandidate("absadef_xxxx"));
EXPECT_GT(m.scoreCandidate("asdf_ASDF"), m.scoreCandidate("asdf_asdf"));
}
}
TEST(FuzzyStringMatcher, CamelCaseScoring) {
// Camel case matches should have higher priority.
{
FuzzyStringMatcher m("mkav");
EXPECT_GT(m.scoreCandidate("MKAnnotationView"),
m.scoreCandidate("MKMapView"));
}
{
FuzzyStringMatcher m("MKAV");
EXPECT_GT(m.scoreCandidate("MKAnnotationView"),
m.scoreCandidate("MKMapView"));
}
{
FuzzyStringMatcher m("moc");
EXPECT_GT(m.scoreCandidate("NSManagedObjectContext"),
m.scoreCandidate("NSManagedobjectcontext"));
EXPECT_GT(m.scoreCandidate("my_one_cat"), m.scoreCandidate("myonecat"));
EXPECT_GT(m.scoreCandidate("my_one_cat"), m.scoreCandidate("aa_bbb_moc"));
EXPECT_GT(m.scoreCandidate("my_one_cat"),
m.scoreCandidate("not_my_one_cat"));
EXPECT_GT(m.scoreCandidate("NSManagedObject+Context"),
m.scoreCandidate("NSManagedobjectcontext"));
EXPECT_GT(m.scoreCandidate("NSManagedObject-Context"),
m.scoreCandidate("NSManagedobjectcontext"));
EXPECT_GT(m.scoreCandidate("NSManagedObjectContextaaa"),
m.scoreCandidate("NSManagedobjectcontextmoc"));
}
{
FuzzyStringMatcher m("m_o_c");
EXPECT_GT(m.scoreCandidate("my_one_cat"),
m.scoreCandidate("not_my_one_cat"));
EXPECT_GT(m.scoreCandidate("my_one_cat"), m.scoreCandidate("my_eno_cat"));
EXPECT_GT(m.scoreCandidate("my_one_cat"),
m.scoreCandidate("not_my_eno_cat"));
}
{
FuzzyStringMatcher m("nsad");
EXPECT_GT(m.scoreCandidate("NSAppDelegate"),
m.scoreCandidate("NSappdelegate"));
}
{
FuzzyStringMatcher m("mws");
EXPECT_GT(m.scoreCandidate("methodWithSelector:"),
m.scoreCandidate("methodwishes:"));
}
{
FuzzyStringMatcher m("cancelDownload");
EXPECT_GT(m.scoreCandidate("cancelDownload"),
m.scoreCandidate("canCancelDownload"));
}
{
FuzzyStringMatcher m("cancelDownload");
EXPECT_GT(m.scoreCandidate("cancelDownload"),
m.scoreCandidate("cancelDownload:"));
}
{
FuzzyStringMatcher m("cancelDownload");
EXPECT_GT(m.scoreCandidate("cancelDownload"),
m.scoreCandidate("setCanCancelDownload"));
}
{
FuzzyStringMatcher m("cancelDownload");
EXPECT_GT(m.scoreCandidate("cancelDownload"),
m.scoreCandidate("setCancelDownloadURL"));
}
}
TEST(FuzzyStringMatcher, LongerRunsInLongerCandidates) {
{
FuzzyStringMatcher m("ABCo");
EXPECT_GT(m.scoreCandidate("ABCooldown"),
m.scoreCandidate("ABCeedIcon"));
}
{
FuzzyStringMatcher m("hasDet");
EXPECT_GT(m.scoreCandidate("hasDetachedOccurrences"),
m.scoreCandidate("bHasDesktopDoodle"));
}
{
FuzzyStringMatcher m("ABProxim");
EXPECT_GT(m.scoreCandidate("ABProximitySensorFoo"),
m.scoreCandidate("ABProxyThatVim"));
}
{
FuzzyStringMatcher m("UIControl");
EXPECT_GT(m.scoreCandidate("UIControl"),
m.scoreCandidate("ABUIController"));
}
}
TEST(FuzzyStringMatcher, ShorterMatches) {
{
FuzzyStringMatcher m("calayer");
EXPECT_GT(m.scoreCandidate("CALayer"), m.scoreCandidate("CALayer_Utility"));
EXPECT_GT(m.scoreCandidate("CALayer"), m.scoreCandidate("CALayerHost"));
EXPECT_GT(m.scoreCandidate("CALayer"), m.scoreCandidate("CALayerPrivate"));
}
{
FuzzyStringMatcher m("NSFileMan");
EXPECT_GT(m.scoreCandidate("NSFileManager"),
m.scoreCandidate("NSFileManagerAdditions"));
EXPECT_GT(m.scoreCandidate("NSFileManager"),
m.scoreCandidate("WebNSFileManagerExtras"));
EXPECT_GT(m.scoreCandidate("NSFileManager"),
m.scoreCandidate("NSFileManagerAdditions"));
}
{
FuzzyStringMatcher m("NSUR");
EXPECT_GT(m.scoreCandidate("NSURL"), m.scoreCandidate("NSURLRequest"));
}
{
FuzzyStringMatcher m("NSCac");
EXPECT_GT(m.scoreCandidate("NSCache"),
m.scoreCandidate("NSCachedImageRep"));
}
}
TEST(FuzzyStringMatcher, SingleCharacterScoring) {
{ // Case sensitive uppercase first character.
FuzzyStringMatcher m("A");
EXPECT_GT(m.scoreCandidate("Aa"), m.scoreCandidate("aa"));
}
{ // Match at start.
FuzzyStringMatcher m("a");
EXPECT_GT(m.scoreCandidate("ab"), m.scoreCandidate("ba"));
}
{ // FIXME: non-first character matches are all the same.
FuzzyStringMatcher m("A");
EXPECT_EQ(m.scoreCandidate("bA"), m.scoreCandidate("ba"));
}
{ // FIXME: non-matches are the same as non-first-character matches.
FuzzyStringMatcher m("a");
EXPECT_EQ(m.scoreCandidate("ba"), m.scoreCandidate("bb"));
EXPECT_TRUE(m.matchesCandidate("ba"));
EXPECT_FALSE(m.matchesCandidate("bb"));
}
}
TEST(FuzzyStringMatcher, ScoringOddities) {
{ // foo doesn't actually match because we jump straight to the last 'O'.
FuzzyStringMatcher m1("foo");
FuzzyStringMatcher m2("el");
EXPECT_GT(m2.scoreCandidate("felDodO"), m1.scoreCandidate("felDodO"));
}
{ // The characters after "." are not counted as part of the % score.
FuzzyStringMatcher m("st");
EXPECT_EQ(m.scoreCandidate("st.A"), m.scoreCandidate("st.AAAAAAAA"));
}
{
FuzzyStringMatcher m1("k_");
FuzzyStringMatcher m2("ka");
EXPECT_GE(m1.scoreCandidate("KU_KA"), m2.scoreCandidate("KU_KA"));
}
{ // The _ after the k seems to have an effect.
FuzzyStringMatcher m1("_k");
FuzzyStringMatcher m2("_w");
EXPECT_GT(m1.scoreCandidate("A_WO_K_"), m2.scoreCandidate("A_WO_K_"));
}
}
TEST(FuzzyStringMatcher, NormalizeSingleCharacterScore) {
FuzzyStringMatcher m("A");
m.normalize = true;
EXPECT_EQ(1.0, m.scoreCandidate("A"));
EXPECT_EQ(0.0, m.scoreCandidate("B"));
EXPECT_GT(1.0, m.scoreCandidate("AB"));
EXPECT_LT(0.0, m.scoreCandidate("AB"));
EXPECT_GT(1.0, m.scoreCandidate("aB"));
EXPECT_LT(0.0, m.scoreCandidate("aB"));
EXPECT_GT(1.0, m.scoreCandidate("aBBBBBBBBBBBBBB"));
EXPECT_LT(0.0, m.scoreCandidate("aBBBBBBBBBBBBBB"));
}
TEST(FuzzyStringMatcher, NormalizeScore) {
FuzzyStringMatcher m("AB");
m.normalize = true;
EXPECT_DOUBLE_EQ(1.0, m.scoreCandidate("AB"));
EXPECT_DOUBLE_EQ(0.0, m.scoreCandidate("BB"));
EXPECT_GT(1.0, m.scoreCandidate("ab"));
EXPECT_LT(0.0, m.scoreCandidate("ab"));
EXPECT_GT(1.0, m.scoreCandidate("ABB"));
EXPECT_LT(0.0, m.scoreCandidate("ABB"));
EXPECT_GT(1.0, m.scoreCandidate("AAB"));
EXPECT_LT(0.0, m.scoreCandidate("AAB"));
EXPECT_GT(1.0, m.scoreCandidate("AaBb"));
EXPECT_LT(0.0, m.scoreCandidate("AaBb"));
EXPECT_GT(1.0, m.scoreCandidate("Aabb"));
EXPECT_LT(0.0, m.scoreCandidate("Aabb"));
FuzzyStringMatcher n("abc");
n.normalize = true;
EXPECT_DOUBLE_EQ(1.0, n.scoreCandidate("abc"));
EXPECT_DOUBLE_EQ(1.0, n.scoreCandidate("ABC"));
}
TEST(FuzzyStringMatcher, TokenizingCharacters) {
FuzzyStringMatcher m("ab");
EXPECT_GT(m.scoreCandidate("a/b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a.b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a_b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a+b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a-b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a:b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a,b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a(b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a)b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a!b"), m.scoreCandidate("acb"));
EXPECT_GT(m.scoreCandidate("a?b"), m.scoreCandidate("acb"));
}
TEST(FuzzyStringMatcher, ShortUnconnectedMatches) {
FuzzyStringMatcher m("abcd");
EXPECT_GT(m.scoreCandidate("xaxbxcdxxxxxx"), m.scoreCandidate("xaxbxcxd"));
EXPECT_GT(m.scoreCandidate("xaxbxc_d"), m.scoreCandidate("xaxbxcxd"));
}