|
24 | 24 | #include <string> |
25 | 25 |
|
26 | 26 | #include "UserOverrideModel.h" |
| 27 | +#include "gramambular2/reading_grid.h" |
27 | 28 | #include "gtest/gtest.h" |
28 | 29 |
|
29 | 30 | namespace McBopomofo { |
@@ -119,4 +120,159 @@ TEST(UserOverrideModelTest, LRUBehavior) { |
119 | 120 | ASSERT_TRUE(v.empty()); |
120 | 121 | } |
121 | 122 |
|
| 123 | +constexpr char kSampleData[] = R"( |
| 124 | +ㄐㄧ 機 -3.02367199 |
| 125 | +ㄐㄧ 積 -3.72854036 |
| 126 | +ㄐㄧ-ㄧㄡˊ 機油 -6.03662914 |
| 127 | +ㄧㄡˊ 由 -3.00970678 |
| 128 | +ㄧㄡˊ 油 -3.75900671 |
| 129 | +)"; |
| 130 | + |
| 131 | +class SimpleLM : public Formosa::Gramambular2::LanguageModel { |
| 132 | + public: |
| 133 | + explicit SimpleLM(const char* input, bool readingIsFirstColumn = true) { |
| 134 | + std::stringstream sstream(input); |
| 135 | + while (sstream.good()) { |
| 136 | + std::string line; |
| 137 | + getline(sstream, line); |
| 138 | + if (line.empty() || line[0] == '#') { |
| 139 | + continue; |
| 140 | + } |
| 141 | + std::stringstream linestream(line); |
| 142 | + std::string col0; |
| 143 | + std::string col1; |
| 144 | + std::string col2; |
| 145 | + linestream >> col0; |
| 146 | + linestream >> col1; |
| 147 | + linestream >> col2; |
| 148 | + db_[readingIsFirstColumn ? col0 : col1].emplace_back( |
| 149 | + readingIsFirstColumn ? col1 : col0, std::stod(col2)); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + std::vector<Unigram> getUnigrams(const std::string& key) override { |
| 154 | + const auto f = db_.find(key); |
| 155 | + return f == db_.end() ? std::vector<Unigram>() : (*f).second; |
| 156 | + } |
| 157 | + |
| 158 | + bool hasUnigrams(const std::string& key) override { |
| 159 | + return db_.find(key) != db_.end(); |
| 160 | + } |
| 161 | + |
| 162 | + protected: |
| 163 | + std::map<std::string, std::vector<Unigram>> db_; |
| 164 | +}; |
| 165 | + |
| 166 | +TEST(UserOverrideModelTest, WalkResultSnapshotTest) { |
| 167 | + // See https://github.com/openvanilla/McBopomofo/issues/885. |
| 168 | + // This is a kind of integration test for the following steps: |
| 169 | + // 1. Type ㄐㄧ ㄧㄡˊ -> 機由; this assumes P(機)P(由) > P(機油) |
| 170 | + // 2. Override with 機油 and tell UOM to observe |
| 171 | + // 3. Reset, type ㄐㄧ ㄧㄡˊ -> 機油 -- UOM should suggest the expected |
| 172 | + // override |
| 173 | + // 4. Reset, type ㄐㄧ -> 機 |
| 174 | + // 5. Override with 積 and tell UOM to observe |
| 175 | + // 6. Reset, type ㄐㄧ ㄧㄡˊ -> 積由 -- this is expected due to UOM suggestion |
| 176 | + // 7. Override with 機油 and tell UOM to observe |
| 177 | + // 8. Reset, type ㄐㄧ -> 積 due to UOM suggestion |
| 178 | + // 9. Continue to type 由 -> walk result should be 積由, and the UOM should |
| 179 | + // suggest 機油 as the override candidate. |
| 180 | + |
| 181 | + std::string sampleData(kSampleData); |
| 182 | + Formosa::Gramambular2::ReadingGrid grid( |
| 183 | + std::make_shared<SimpleLM>(sampleData.c_str())); |
| 184 | + UserOverrideModel uom(kCapacity, kHalflife); |
| 185 | + Formosa::Gramambular2::ReadingGrid::WalkResult walkBefore; |
| 186 | + Formosa::Gramambular2::ReadingGrid::WalkResult walkLatest; |
| 187 | + UserOverrideModel::Suggestion suggestion; |
| 188 | + std::vector<Formosa::Gramambular2::ReadingGrid::NodePtr>::const_iterator |
| 189 | + nodeIter; |
| 190 | + double timestamp = kFakeNow; |
| 191 | + |
| 192 | + grid.insertReading("ㄐㄧ"); |
| 193 | + grid.insertReading("ㄧㄡˊ"); |
| 194 | + walkBefore = grid.walk(); |
| 195 | + ASSERT_EQ(walkBefore.valuesAsStrings(), |
| 196 | + (std::vector<std::string>{"機", "由"})); |
| 197 | + |
| 198 | + grid.overrideCandidate(1, "機油"); |
| 199 | + walkLatest = grid.walk(); |
| 200 | + ASSERT_EQ(walkLatest.valuesAsStrings(), (std::vector<std::string>{"機油"})); |
| 201 | + |
| 202 | + nodeIter = walkLatest.findNodeAt(1, /*outCursorPastNode=*/nullptr); |
| 203 | + ASSERT_NE(nodeIter, walkLatest.nodes.cend()); |
| 204 | + uom.observe(walkBefore, walkLatest, 1, timestamp); |
| 205 | + timestamp += 1.0; |
| 206 | + |
| 207 | + grid.clear(); |
| 208 | + grid.insertReading("ㄐㄧ"); |
| 209 | + grid.insertReading("ㄧㄡˊ"); |
| 210 | + walkLatest = grid.walk(); |
| 211 | + suggestion = uom.suggest(walkLatest, 1, timestamp); |
| 212 | + timestamp += 1.0; |
| 213 | + ASSERT_EQ(suggestion.candidate, "機油"); |
| 214 | + |
| 215 | + grid.clear(); |
| 216 | + grid.insertReading("ㄐㄧ"); |
| 217 | + walkBefore = grid.walk(); |
| 218 | + ASSERT_EQ(walkBefore.valuesAsStrings(), (std::vector<std::string>{"機"})); |
| 219 | + |
| 220 | + grid.overrideCandidate(0, "積"); |
| 221 | + walkLatest = grid.walk(); |
| 222 | + ASSERT_EQ(walkLatest.valuesAsStrings(), (std::vector<std::string>{"積"})); |
| 223 | + |
| 224 | + nodeIter = walkLatest.findNodeAt(0, /*outCursorPastNode=*/nullptr); |
| 225 | + ASSERT_NE(nodeIter, walkLatest.nodes.cend()); |
| 226 | + uom.observe(walkBefore, walkLatest, 0, timestamp); |
| 227 | + timestamp += 1.0; |
| 228 | + |
| 229 | + grid.clear(); |
| 230 | + grid.insertReading("ㄐㄧ"); |
| 231 | + walkLatest = grid.walk(); |
| 232 | + suggestion = uom.suggest(walkLatest, 0, timestamp); |
| 233 | + timestamp += 1.0; |
| 234 | + ASSERT_EQ(suggestion.candidate, "積"); |
| 235 | + |
| 236 | + grid.overrideCandidate(0, "積"); |
| 237 | + grid.insertReading("ㄧㄡˊ"); |
| 238 | + |
| 239 | + // THIS MUST BE A COPY OF NODES, otherwise the observation below will not |
| 240 | + // capture the correct state of the grid. |
| 241 | + // See https://github.com/openvanilla/McBopomofo/issues/885. |
| 242 | + // |
| 243 | + // This is wrong: |
| 244 | + // walkBefore = grid.walk(); |
| 245 | + // |
| 246 | + // The following is correct: |
| 247 | + walkBefore = grid.walk().copyWithFixedNodes(); |
| 248 | + |
| 249 | + ASSERT_EQ(walkBefore.valuesAsStrings(), |
| 250 | + (std::vector<std::string>{"積", "由"})); |
| 251 | + |
| 252 | + grid.overrideCandidate(1, "機油"); |
| 253 | + walkLatest = grid.walk(); |
| 254 | + ASSERT_EQ(walkLatest.valuesAsStrings(), (std::vector<std::string>{"機油"})); |
| 255 | + |
| 256 | + nodeIter = walkLatest.findNodeAt(1, /*outCursorPastNode=*/nullptr); |
| 257 | + ASSERT_NE(nodeIter, walkLatest.nodes.cend()); |
| 258 | + uom.observe(walkBefore, walkLatest, 1, timestamp); |
| 259 | + timestamp += 1.0; |
| 260 | + |
| 261 | + grid.clear(); |
| 262 | + grid.insertReading("ㄐㄧ"); |
| 263 | + walkLatest = grid.walk(); |
| 264 | + suggestion = uom.suggest(walkLatest, 0, timestamp); |
| 265 | + timestamp += 1.0; |
| 266 | + ASSERT_EQ(suggestion.candidate, "積"); |
| 267 | + grid.overrideCandidate(0, "積"); |
| 268 | + grid.insertReading("ㄧㄡˊ"); |
| 269 | + walkLatest = grid.walk(); |
| 270 | + ASSERT_EQ(walkLatest.valuesAsStrings(), |
| 271 | + (std::vector<std::string>{"積", "由"})); |
| 272 | + |
| 273 | + suggestion = uom.suggest(walkLatest, 1, timestamp); |
| 274 | + timestamp += 1.0; |
| 275 | + ASSERT_EQ(suggestion.candidate, "機油"); |
| 276 | +} |
| 277 | + |
122 | 278 | } // namespace McBopomofo |
0 commit comments