blob: 35a5e796f5affb8a70e1efeddfe7ca9067552737 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/editing/state_machines/state_machine_test_util.h"
#include <algorithm>
#include "third_party/blink/renderer/core/editing/state_machines/backward_grapheme_boundary_state_machine.h"
#include "third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.h"
#include "third_party/blink/renderer/core/editing/state_machines/text_segmentation_machine_state.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
namespace {
char MachineStateToChar(TextSegmentationMachineState state) {
static const char kIndicators[] = {
'I', // Invalid
'R', // NeedMoreCodeUnit (Repeat)
'S', // NeedFollowingCodeUnit (Switch)
'F', // Finished
};
auto* const it = std::begin(kIndicators) + static_cast<size_t>(state);
DCHECK_GE(it, std::begin(kIndicators)) << "Unknown backspace value";
DCHECK_LT(it, std::end(kIndicators)) << "Unknown backspace value";
return *it;
}
Vector<UChar> CodePointsToCodeUnits(const Vector<UChar32>& code_points) {
Vector<UChar> out;
for (const auto& code_point : code_points) {
if (U16_LENGTH(code_point) == 2) {
out.push_back(U16_LEAD(code_point));
out.push_back(U16_TRAIL(code_point));
} else {
out.push_back(static_cast<UChar>(code_point));
}
}
return out;
}
template <typename StateMachine>
String ProcessSequence(StateMachine* machine,
const Vector<UChar32>& preceding,
const Vector<UChar32>& following) {
machine->Reset();
StringBuilder out;
TextSegmentationMachineState state = TextSegmentationMachineState::kInvalid;
Vector<UChar> preceding_code_units = CodePointsToCodeUnits(preceding);
std::reverse(preceding_code_units.begin(), preceding_code_units.end());
for (const auto& code_unit : preceding_code_units) {
state = machine->FeedPrecedingCodeUnit(code_unit);
out.Append(MachineStateToChar(state));
switch (state) {
case TextSegmentationMachineState::kInvalid:
case TextSegmentationMachineState::kFinished:
return out.ToString();
case TextSegmentationMachineState::kNeedMoreCodeUnit:
continue;
case TextSegmentationMachineState::kNeedFollowingCodeUnit:
break;
}
}
if (preceding.IsEmpty() ||
state == TextSegmentationMachineState::kNeedMoreCodeUnit) {
state = machine->TellEndOfPrecedingText();
out.Append(MachineStateToChar(state));
}
if (state == TextSegmentationMachineState::kFinished)
return out.ToString();
Vector<UChar> following_code_units = CodePointsToCodeUnits(following);
for (const auto& code_unit : following_code_units) {
state = machine->FeedFollowingCodeUnit(code_unit);
out.Append(MachineStateToChar(state));
switch (state) {
case TextSegmentationMachineState::kInvalid:
case TextSegmentationMachineState::kFinished:
return out.ToString();
case TextSegmentationMachineState::kNeedMoreCodeUnit:
continue;
case TextSegmentationMachineState::kNeedFollowingCodeUnit:
break;
}
}
return out.ToString();
}
} // namespace
String GraphemeStateMachineTestBase::ProcessSequenceBackward(
BackwardGraphemeBoundaryStateMachine* machine,
const Vector<UChar32>& preceding) {
const String& out = ProcessSequence(machine, preceding, Vector<UChar32>());
if (machine->FinalizeAndGetBoundaryOffset() !=
machine->FinalizeAndGetBoundaryOffset())
return "State machine changes final offset after finished.";
return out;
}
String GraphemeStateMachineTestBase::ProcessSequenceForward(
ForwardGraphemeBoundaryStateMachine* machine,
const Vector<UChar32>& preceding,
const Vector<UChar32>& following) {
const String& out = ProcessSequence(machine, preceding, following);
if (machine->FinalizeAndGetBoundaryOffset() !=
machine->FinalizeAndGetBoundaryOffset())
return "State machine changes final offset after finished.";
return out;
}
} // namespace blink