1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/editing/state_machines/state_machine_test_util.h"
6 
7 #include <algorithm>
8 #include "third_party/blink/renderer/core/editing/state_machines/backward_grapheme_boundary_state_machine.h"
9 #include "third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.h"
10 #include "third_party/blink/renderer/core/editing/state_machines/text_segmentation_machine_state.h"
11 #include "third_party/blink/renderer/platform/wtf/assertions.h"
12 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
13 
14 namespace blink {
15 
16 namespace {
MachineStateToChar(TextSegmentationMachineState state)17 char MachineStateToChar(TextSegmentationMachineState state) {
18   static const char kIndicators[] = {
19       'I',  // Invalid
20       'R',  // NeedMoreCodeUnit (Repeat)
21       'S',  // NeedFollowingCodeUnit (Switch)
22       'F',  // Finished
23   };
24   auto* const it = std::begin(kIndicators) + static_cast<size_t>(state);
25   DCHECK_GE(it, std::begin(kIndicators)) << "Unknown backspace value";
26   DCHECK_LT(it, std::end(kIndicators)) << "Unknown backspace value";
27   return *it;
28 }
29 
CodePointsToCodeUnits(const Vector<UChar32> & code_points)30 Vector<UChar> CodePointsToCodeUnits(const Vector<UChar32>& code_points) {
31   Vector<UChar> out;
32   for (const auto& code_point : code_points) {
33     if (U16_LENGTH(code_point) == 2) {
34       out.push_back(U16_LEAD(code_point));
35       out.push_back(U16_TRAIL(code_point));
36     } else {
37       out.push_back(static_cast<UChar>(code_point));
38     }
39   }
40   return out;
41 }
42 
43 template <typename StateMachine>
ProcessSequence(StateMachine * machine,const Vector<UChar32> & preceding,const Vector<UChar32> & following)44 String ProcessSequence(StateMachine* machine,
45                        const Vector<UChar32>& preceding,
46                        const Vector<UChar32>& following) {
47   machine->Reset();
48   StringBuilder out;
49   TextSegmentationMachineState state = TextSegmentationMachineState::kInvalid;
50   Vector<UChar> preceding_code_units = CodePointsToCodeUnits(preceding);
51   std::reverse(preceding_code_units.begin(), preceding_code_units.end());
52   for (const auto& code_unit : preceding_code_units) {
53     state = machine->FeedPrecedingCodeUnit(code_unit);
54     out.Append(MachineStateToChar(state));
55     switch (state) {
56       case TextSegmentationMachineState::kInvalid:
57       case TextSegmentationMachineState::kFinished:
58         return out.ToString();
59       case TextSegmentationMachineState::kNeedMoreCodeUnit:
60         continue;
61       case TextSegmentationMachineState::kNeedFollowingCodeUnit:
62         break;
63     }
64   }
65   if (preceding.IsEmpty() ||
66       state == TextSegmentationMachineState::kNeedMoreCodeUnit) {
67     state = machine->TellEndOfPrecedingText();
68     out.Append(MachineStateToChar(state));
69   }
70   if (state == TextSegmentationMachineState::kFinished)
71     return out.ToString();
72 
73   Vector<UChar> following_code_units = CodePointsToCodeUnits(following);
74   for (const auto& code_unit : following_code_units) {
75     state = machine->FeedFollowingCodeUnit(code_unit);
76     out.Append(MachineStateToChar(state));
77     switch (state) {
78       case TextSegmentationMachineState::kInvalid:
79       case TextSegmentationMachineState::kFinished:
80         return out.ToString();
81       case TextSegmentationMachineState::kNeedMoreCodeUnit:
82         continue;
83       case TextSegmentationMachineState::kNeedFollowingCodeUnit:
84         break;
85     }
86   }
87   return out.ToString();
88 }
89 }  // namespace
90 
ProcessSequenceBackward(BackwardGraphemeBoundaryStateMachine * machine,const Vector<UChar32> & preceding)91 String GraphemeStateMachineTestBase::ProcessSequenceBackward(
92     BackwardGraphemeBoundaryStateMachine* machine,
93     const Vector<UChar32>& preceding) {
94   const String& out = ProcessSequence(machine, preceding, Vector<UChar32>());
95   if (machine->FinalizeAndGetBoundaryOffset() !=
96       machine->FinalizeAndGetBoundaryOffset())
97     return "State machine changes final offset after finished.";
98   return out;
99 }
100 
ProcessSequenceForward(ForwardGraphemeBoundaryStateMachine * machine,const Vector<UChar32> & preceding,const Vector<UChar32> & following)101 String GraphemeStateMachineTestBase::ProcessSequenceForward(
102     ForwardGraphemeBoundaryStateMachine* machine,
103     const Vector<UChar32>& preceding,
104     const Vector<UChar32>& following) {
105   const String& out = ProcessSequence(machine, preceding, following);
106   if (machine->FinalizeAndGetBoundaryOffset() !=
107       machine->FinalizeAndGetBoundaryOffset())
108     return "State machine changes final offset after finished.";
109   return out;
110 }
111 
112 }  // namespace blink
113