1 /* 2 * SessionFindTests.cpp 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant 7 * to the terms of a commercial license agreement with RStudio, then 8 * this program is licensed to you under the terms of version 3 of the 9 * GNU Affero General Public License. This program is distributed WITHOUT 10 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, 11 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the 12 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. 13 * 14 */ 15 16 #include "SessionFind.hpp" 17 18 #include <core/system/ShellUtils.hpp> 19 20 #define RSTUDIO_NO_TESTTHAT_ALIASES 21 #include <tests/TestThat.hpp> 22 23 namespace rstudio { 24 namespace session { 25 namespace modules { 26 namespace find { 27 namespace tests { 28 29 using namespace rstudio::core; 30 using namespace modules::find; 31 32 namespace { 33 34 const std::string kLine("RStudio is great"); 35 const std::string kReplaceString("awesome"); 36 const size_t matchOn = 11; 37 const size_t matchOff = 16; 38 39 const std::string kRegexLine("aba OOOkkk okab AAOO aaabbb aa abab"); 40 const size_t rMatchOn = 4; 41 const size_t rMatchOff = 10; 42 const size_t caseMatchOn = 21; 43 const size_t caseMatchOff = 27; 44 45 const std::string kFindRegex("\\([a-z]\\)\\1\\{2\\}\\([a-z]\\)\\2\\{2\\}"); 46 const std::string kReplaceRegex("\\1\\2\\1\\2"); 47 48 const std::string kGrepPattern("aba \033[01m\033[KOOOkkk\033[m\033[K okab AAOO awesome aa abab"); 49 const std::string kGitGrepPattern("aba \033[1;31mOOOkkk\033[m okab AAOO awesome aa abab"); 50 } // anonymous namespace 51 52 TEST_CASE("SessionFind") 53 { 54 SECTION("Replace literal ignore case") 55 { 56 std::string line(kLine); 57 size_t replaceMatchOff; 58 59 Replacer replacer(true); 60 replacer.replaceLiteral(matchOn, matchOff, 61 kReplaceString, &line, &replaceMatchOff); 62 CHECK(line.compare("RStudio is awesome") == 0); 63 CHECK(replaceMatchOff == 18); 64 } 65 66 SECTION("Replace regex case sensitive") 67 { 68 std::string line(kRegexLine); 69 size_t replaceMatchOff; 70 71 Replacer replacer(false); 72 replacer.replaceRegex(caseMatchOn, caseMatchOff, 73 kFindRegex, kReplaceString, &line, &replaceMatchOff); 74 CHECK(line.compare("aba OOOkkk okab AAOO awesome aa abab") == 0); 75 CHECK(replaceMatchOff == 28); 76 } 77 78 SECTION("Replace regex ignore case") 79 { 80 std::string line(kRegexLine); 81 size_t replaceMatchOff; 82 83 Replacer replacer(true); 84 replacer.replaceRegex(rMatchOn, rMatchOff, kFindRegex, kReplaceRegex, &line, 85 &replaceMatchOff); 86 CHECK(line.compare("aba OkOk okab AAOO aaabbb aa abab") == 0); 87 CHECK(replaceMatchOff == 8); 88 89 size_t matchOn = 18; 90 size_t matchOff = 24; 91 replacer.replaceRegex(matchOn, matchOff, kFindRegex, kReplaceRegex, &line, 92 &replaceMatchOff); 93 CHECK(line.compare("aba OkOk okab AAOO abab aa abab") == 0); 94 CHECK(replaceMatchOff == 22); 95 } 96 97 SECTION("Replace regex case sensitive") 98 { 99 std::string line(kRegexLine); 100 size_t replaceMatchOff; 101 102 Replacer replacer(false); 103 replacer.replaceRegex(caseMatchOn, caseMatchOff, kFindRegex, kReplaceRegex, &line, 104 &replaceMatchOff); 105 CHECK(line.compare("aba OOOkkk okab AAOO abab aa abab") == 0); 106 CHECK(replaceMatchOff == 25); 107 } 108 109 SECTION("Replace ASCII encoding") 110 { 111 std::string line("äSCII ìs ƒun"); 112 std::string find("ƒun"); 113 std::string replace("Ök"); 114 115 size_t matchOn = 11; 116 size_t matchOff = 15; 117 size_t replaceMatchOff; 118 119 Replacer replacer(false, "ASCII"); 120 replacer.replaceRegex(matchOn, matchOff, find, replace, &line, &replaceMatchOff); 121 CHECK(line.compare("äSCII ìs Ök") == 0); 122 CHECK(replaceMatchOff == 14); 123 } 124 125 SECTION("Replace BIG5 encoding") 126 { 127 std::string line("´sπƒ∆GƒßµM"); 128 std::string find("∆G"); 129 std::string replace("…@"); 130 131 size_t matchOn = 7; 132 size_t matchOff = 11; 133 size_t replaceMatchOff; 134 135 Replacer replacer(false, "BIG5"); 136 replacer.replaceRegex(matchOn, matchOff, find, replace, &line, &replaceMatchOff); 137 CHECK(line.compare("´sπƒ…@ƒßµM") == 0); 138 CHECK(replaceMatchOff == 11); 139 140 } 141 142 SECTION("Attempt replace without valid match") 143 { 144 std::string line(kLine); 145 size_t replaceMatchOff = 99; 146 147 Replacer replacer(true); 148 Error error = replacer.replaceRegex(rMatchOn, rMatchOff, kFindRegex, kReplaceRegex, 149 &line, &replaceMatchOff); 150 CHECK(line.compare(kLine) == 0); 151 } 152 153 SECTION("Attempt replace with consecutive matches") 154 { 155 std::string line("hellohellohello"); 156 std::string replacePattern("hello world"); 157 size_t replaceMatchOff; 158 size_t on = 10; 159 size_t off = 15; 160 161 Replacer replacer(true); 162 replacer.replaceLiteral(on, off, replacePattern, &line, &replaceMatchOff); 163 CHECK(line.compare("hellohellohello world") == 0); 164 CHECK(replaceMatchOff == 21); 165 166 on = 5; 167 off = 10; 168 replacer.replaceRegex(on, off, "hello", replacePattern, &line, &replaceMatchOff); 169 CHECK(line.compare("hellohello worldhello world") == 0); 170 CHECK(replaceMatchOff == 16); 171 172 on = 0; 173 off = 5; 174 replacer.replaceRegex(on, off, "hello", replacePattern, &line, &replaceMatchOff); 175 CHECK(line.compare("hello worldhello worldhello world") == 0); 176 CHECK(replaceMatchOff == 11); 177 } 178 179 SECTION("Attempt regex replace with nested results") 180 { 181 std::string line("hehello worldllo"); 182 std::string findPattern("he[^ ].*llo"); 183 std::string replacePattern("hello world"); 184 size_t replaceMatchOff; 185 size_t on = 0; 186 size_t off = 16; 187 188 Replacer replacer(false); 189 replacer.replaceRegex(on, off, findPattern, replacePattern, &line, &replaceMatchOff); 190 CHECK(line.compare("hello world") == 0); 191 CHECK(replaceMatchOff == 11); 192 } 193 194 SECTION("Grep get file, line number, and contents") 195 { 196 boost::regex regex = getGrepOutputRegex(/*isGitGrep*/ false); 197 std::string contents( 198 "case.test:2:aba \033[01m\033[KOOOkkk\033[m\033[K okab AAOO awesome aa abab"); 199 200 boost::smatch match; 201 CHECK(regex_utils::match(contents, match, regex)); 202 CHECK(match[1].str().compare("case.test") == 0); 203 CHECK(match[2].str().compare("2") == 0); 204 CHECK(match[3].str().compare(kGrepPattern) == 0); 205 } 206 207 SECTION("Grep get color encoding regex") 208 { 209 boost::regex regex = getColorEncodingRegex(/*isGitGrep*/ false); 210 boost::cmatch match; 211 CHECK(regex_utils::search(kGrepPattern.c_str(), match, regex)); 212 CHECK(match[1].str().compare("01") == 0); 213 } 214 215 SECTION("Git grep get file, line number, and contents") 216 { 217 boost::regex regex = getGrepOutputRegex(/*isGitGrep*/ true); 218 std::string contents( 219 "case.test\033[36m:\033[m2\033[36m:\033[maba \033[1;31mOOOkkk\033[m okab AAOO awesome aa abab"); 220 221 boost::smatch match; 222 CHECK(regex_utils::match(contents, match, regex)); 223 CHECK(match[1].str().compare("case.test") == 0); 224 CHECK(match[2].str().compare("2") == 0); 225 CHECK(match[3].str().compare(kGitGrepPattern) == 0); 226 } 227 228 SECTION("Git grep with colon get file, line number, and contents") 229 { 230 boost::regex regex = getGrepOutputRegex(/*isGitGrep*/ true); 231 std::string contents( 232 "file.test\033[36m:\033[m9\033[36m:\033[m - \033[1;31mr:\033[m devel"); 233 234 boost::smatch match; 235 CHECK(regex_utils::match(contents, match, regex)); 236 CHECK(match[1].str().compare("file.test") == 0); 237 CHECK(match[2].str().compare("9") == 0); 238 CHECK(match[3].str().compare(" - \033[1;31mr:\033[m devel") == 0); 239 } 240 241 SECTION("Git grep get color encoding regex") 242 { 243 boost::regex regex = getColorEncodingRegex(/*isGitGrep*/ true); 244 boost::cmatch match; 245 CHECK(regex_utils::search(kGitGrepPattern.c_str(), match, regex)); 246 CHECK(match[2].str().compare("1") == 0); 247 } 248 249 } 250 251 } // end namespace tests 252 } // end namespace modules 253 } // end namespace find 254 } // end namespace session 255 } // end namespace rstudio 256 257