blob: 822a10dacc4c8294292af47ff31a03cf0ef71277 [file] [log] [blame]
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "common/formatting/token_partition_tree.h"
#include <iosfwd>
#include <iterator>
#include <string>
#include <vector>
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "common/formatting/format_token.h"
#include "common/formatting/token_partition_tree_test_utils.h"
#include "common/formatting/unwrapped_line.h"
#include "common/formatting/unwrapped_line_test_utils.h"
#include "common/util/container_iterator_range.h"
#include "common/util/tree_operations.h"
#include "common/util/vector_tree.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace verible {
namespace {
using ::testing::ElementsAre;
// Helper class that initializes an array of tokens to be partitioned
// into TokenPartitionTree.
class TokenPartitionTreeTestFixture : public ::testing::Test,
public UnwrappedLineMemoryHandler {
public:
TokenPartitionTreeTestFixture()
: sample_("one two three four five six"),
tokens_(absl::StrSplit(sample_, ' ')) {
for (const auto token : tokens_) {
ftokens_.emplace_back(TokenInfo{1, token});
}
CreateTokenInfos(ftokens_);
}
protected:
const std::string sample_;
const std::vector<absl::string_view> tokens_;
std::vector<TokenInfo> ftokens_;
};
class VerifyFullTreeFormatTokenRangesTest
: public TokenPartitionTreeTestFixture {};
TEST_F(VerifyFullTreeFormatTokenRangesTest, ParentChildRangeBeginMismatch) {
// Construct three partitions, sizes: 1, 3, 2
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// one short of partition0's begin, and will fail invariant.
UnwrappedLine all(0, begin + 1);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition2(0, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
EXPECT_DEATH(VerifyFullTreeFormatTokenRanges(tree, begin),
"Check failed: parent_begin == children_begin");
}
TEST_F(VerifyFullTreeFormatTokenRangesTest, ParentChildRangeEndMismatch) {
// Construct three partitions, sizes: 1, 3, 2
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
// one short of partition2's end, and will fail invariant.
all.SpanUpToToken(preformat_tokens.end() - 1);
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition2(0, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
EXPECT_DEATH(VerifyFullTreeFormatTokenRanges(tree, begin),
"Check failed: parent_end == children_end");
}
TEST_F(VerifyFullTreeFormatTokenRangesTest, ChildrenRangeDiscontinuity) {
// Construct three partitions, sizes: 1, 3, 2
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 3);
// There is a gap between partition1 and partition2 that fails an invariant.
UnwrappedLine partition2(0, partition1.TokensRange().end() + 1);
partition2.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
EXPECT_DEATH(VerifyFullTreeFormatTokenRanges(tree, begin),
"Check failed: current_begin == previous_end");
}
class FindLargestPartitionsTest : public TokenPartitionTreeTestFixture {};
// Verify that this finds the K largest leaf partitions.
TEST_F(FindLargestPartitionsTest, VectorTree) {
// Construct three partitions, sizes: 1, 3, 2
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition2(0, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
// Verify sizes of two largest leaf partitions.
const auto top_two = FindLargestPartitions(tree, 2);
EXPECT_EQ(top_two[0]->Size(), 3);
EXPECT_EQ(top_two[0]->TokensRange().front().Text(), "two");
EXPECT_EQ(top_two[1]->Size(), 2);
EXPECT_EQ(top_two[1]->TokensRange().front().Text(), "five");
}
class FlushLeftSpacingDifferencesTest : public TokenPartitionTreeTestFixture {};
TEST_F(FlushLeftSpacingDifferencesTest, EmptyPartitions) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
TokenPartitionTree tree{all}; // no children
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
using V = std::vector<int>;
const std::vector<V> diffs(FlushLeftSpacingDifferences(range));
EXPECT_THAT(diffs, ElementsAre());
}
TEST_F(FlushLeftSpacingDifferencesTest, OnePartitionsOneToken) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(begin + 1);
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
using T = TokenPartitionTree;
T tree{partition0, T{partition0}}; // one partition, one token
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
using V = std::vector<int>;
const std::vector<V> diffs(FlushLeftSpacingDifferences(range));
EXPECT_THAT(diffs, ElementsAre(V()));
}
TEST_F(FlushLeftSpacingDifferencesTest, OnePartitionsTwoTokens) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(begin + 2);
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 2);
using T = TokenPartitionTree;
T tree{partition0, T{partition0}}; // one partition, two tokens
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
using V = std::vector<int>;
const std::vector<V> diffs(FlushLeftSpacingDifferences(range));
EXPECT_THAT(diffs, ElementsAre(V({0})));
}
TEST_F(FlushLeftSpacingDifferencesTest, TwoPartitionsOneTokenEach) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(begin + 2);
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, begin + 1);
partition1.SpanUpToToken(begin + 2);
using T = TokenPartitionTree;
T tree{partition0, T{partition0}, T{partition1}}; // two partitions
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
using V = std::vector<int>;
const std::vector<V> diffs(FlushLeftSpacingDifferences(range));
EXPECT_THAT(diffs, ElementsAre(V(), V()));
}
TEST_F(FlushLeftSpacingDifferencesTest, TwoPartitionsTwoTokensEach) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(begin + 4);
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 2);
UnwrappedLine partition1(0, begin + 2);
partition1.SpanUpToToken(begin + 4);
using T = TokenPartitionTree;
T tree{partition0, T{partition0}, T{partition1}}; // two partitions
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
using V = std::vector<int>;
const std::vector<V> diffs(FlushLeftSpacingDifferences(range));
EXPECT_THAT(diffs, ElementsAre(V({0}), V({0})));
}
class TokenPartitionTreePrinterTest : public TokenPartitionTreeTestFixture {};
// Verify specialized tree printing of UnwrappedLines.
TEST_F(TokenPartitionTreePrinterTest, VectorTreeShallow) {
// Construct three partitions, sizes: 1, 3, 2
// Change some attributes that will appear in the verbose rendition.
pre_format_tokens_[2].before.spaces_required = 1;
pre_format_tokens_[3].before.break_decision = SpacingOptions::MustAppend;
pre_format_tokens_[4].before.break_penalty = 22;
pre_format_tokens_[5].before.break_decision = SpacingOptions::MustWrap;
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(2, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(2, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition2(2, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
{
std::ostringstream stream;
stream << TokenPartitionTreePrinter(tree);
EXPECT_EQ(stream.str(), R"({ ([<auto>], policy: uninitialized) @{}
{ (>>[one], policy: uninitialized) }
{ (>>[two three four], policy: uninitialized) }
{ (>>[five six], policy: uninitialized) }
})");
}
{
std::ostringstream stream;
stream << TokenPartitionTreePrinter(tree, true); // verbose
EXPECT_EQ(stream.str(), R"({ ([<auto>], policy: uninitialized) @{}
{ (>>[<_0,0>one], policy: uninitialized) }
{ (>>[<_0,0>two <_1,0>three <+_0>four], policy: uninitialized) }
{ (>>[<_0,22>five <\n>six], policy: uninitialized) }
})");
}
}
TEST_F(TokenPartitionTreePrinterTest, VectorTreeDeep) {
// Construct partitions, sizes: 1, (2, 1), (1, 1)
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(2, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(2, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition1a(4, partition1.TokensRange().begin());
partition1a.SpanUpToToken(begin + 3);
UnwrappedLine partition1b(4, partition1a.TokensRange().end());
partition1b.SpanUpToToken(begin + 4);
UnwrappedLine partition2(2, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition2a(4, partition2.TokensRange().begin());
partition2a.SpanUpToToken(begin + 5);
UnwrappedLine partition2b(4, partition2a.TokensRange().end());
partition2b.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{
partition1,
tree_type{partition1a},
tree_type{partition1b},
},
tree_type{
partition2,
tree_type{partition2a},
tree_type{partition2b},
},
};
std::ostringstream stream;
stream << TokenPartitionTreePrinter(tree);
EXPECT_EQ(stream.str(), R"({ ([<auto>], policy: uninitialized) @{}
{ (>>[one], policy: uninitialized) }
{ (>>[<auto>], policy: uninitialized) @{1}
{ (>>>>[two three], policy: uninitialized) }
{ (>>>>[four], policy: uninitialized) }
}
{ (>>[<auto>], policy: uninitialized) @{2}
{ (>>>>[five], policy: uninitialized) }
{ (>>>>[six], policy: uninitialized) }
}
})");
}
class AdjustIndentationTest : public TokenPartitionTreeTestFixture {};
TEST_F(AdjustIndentationTest, Relative) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(5, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine sub(7, begin);
sub.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all, //
tree_type{sub}, // same range, but indented more
};
EXPECT_EQ(tree.Value().IndentationSpaces(), 5);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 7);
AdjustIndentationRelative(&tree, 1);
EXPECT_EQ(tree.Value().IndentationSpaces(), 6);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 8);
AdjustIndentationRelative(&tree, -3);
EXPECT_EQ(tree.Value().IndentationSpaces(), 3);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 5);
AdjustIndentationRelative(&tree, -4);
EXPECT_EQ(tree.Value().IndentationSpaces(), 0); // clamped at 0
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 1);
}
TEST_F(AdjustIndentationTest, Absolute) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(5, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine sub(7, begin);
sub.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all, //
tree_type{sub}, // same range, but indented more
};
EXPECT_EQ(tree.Value().IndentationSpaces(), 5);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 7);
AdjustIndentationAbsolute(&tree, 5); // no change
EXPECT_EQ(tree.Value().IndentationSpaces(), 5);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 7);
AdjustIndentationAbsolute(&tree, 8);
EXPECT_EQ(tree.Value().IndentationSpaces(), 8);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 10);
AdjustIndentationAbsolute(&tree, 0);
EXPECT_EQ(tree.Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children().front().Value().IndentationSpaces(), 2);
}
static bool PropertiesEqual(const UnwrappedLine& left,
const UnwrappedLine& right) {
return (left.TokensRange() == right.TokensRange() &&
left.IndentationSpaces() == right.IndentationSpaces() &&
left.PartitionPolicy() == right.PartitionPolicy());
}
class GroupLeafWithPreviousLeafTest : public TokenPartitionTreeTestFixture {};
TEST_F(GroupLeafWithPreviousLeafTest, RootOnly) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
};
const auto saved_tree(tree); // deep copy
auto* group = GroupLeafWithPreviousLeaf(&tree);
EXPECT_EQ(group, nullptr);
// Expect no change.
const auto diff = DeepEqual(tree, saved_tree, PropertiesEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(GroupLeafWithPreviousLeafTest, OneChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all, tree_type{all}, // subtree spans same range
};
ASSERT_FALSE(is_leaf(tree));
const auto saved_tree(tree); // deep copy
auto* group = GroupLeafWithPreviousLeaf(&tree.Children().front());
EXPECT_EQ(group, nullptr);
// Expect no change.
const auto diff = DeepEqual(tree, saved_tree, PropertiesEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(GroupLeafWithPreviousLeafTest, TwoChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
part1.SetIndentationSpaces(4);
part1.SetPartitionPolicy(PartitionPolicyEnum::kAlwaysExpand);
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
part2.SetIndentationSpaces(2);
part2.SetPartitionPolicy(PartitionPolicyEnum::kFitOnLineElseExpand);
UnwrappedLine group_part(0, begin);
group_part.SpanUpToToken(preformat_tokens.end());
group_part.SetIndentationSpaces(4);
group_part.SetPartitionPolicy(PartitionPolicyEnum::kAlwaysExpand);
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{part1},
tree_type{part2},
};
const tree_type expected_tree{
all,
tree_type{
group_part,
tree_type{part1},
tree_type{part2},
},
};
auto* group = GroupLeafWithPreviousLeaf(&tree.Children().back());
EXPECT_EQ(group, &tree.Children().back());
const auto diff = DeepEqual(tree, expected_tree, PropertiesEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(GroupLeafWithPreviousLeafTest, TwoGenerations) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part1b + part2
UnwrappedLine group_part(0, part1b.TokensRange().begin());
group_part.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1, tree_type{part1a}, // unchanged
tree_type{part1b}, // target partition
},
tree_type{part2}, // source partition
};
const tree_type expected_tree{
all,
tree_type{
all,
tree_type{part1a},
tree_type{
group_part, tree_type{part1b}, // target partition
tree_type{part2}, // source partition
},
},
};
auto* group = GroupLeafWithPreviousLeaf(&tree.Children().back());
EXPECT_EQ(group, &tree.Children().back().Children().back());
const auto diff = DeepEqual(tree, expected_tree, PropertiesEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(GroupLeafWithPreviousLeafTest, Cousins) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
UnwrappedLine part2a(0, part2.TokensRange().begin());
part2a.SpanUpToToken(part2.TokensRange().begin() + 1);
UnwrappedLine part2b(0, part2a.TokensRange().end());
part2b.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part1b + part2a
UnwrappedLine group_part(0, part1b.TokensRange().begin());
group_part.SpanUpToToken(part2a.TokensRange().end());
UnwrappedLine group_parent(0, begin);
group_parent.SpanUpToToken(part2a.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1, tree_type{part1a}, // unchanged
tree_type{part1b}, // target partition
},
tree_type{
part2,
tree_type{part2a}, // source partition
tree_type{part2b},
},
};
const tree_type expected_tree{
all,
tree_type{
group_parent,
tree_type{part1a},
tree_type{
group_part, tree_type{part1b}, // target partition
tree_type{part2a}, // source partition
},
},
tree_type{
part2b,
tree_type{part2b},
},
};
auto* group =
GroupLeafWithPreviousLeaf(&tree.Children().back().Children().front());
EXPECT_EQ(group, &tree.Children().front().Children().back());
const auto diff = DeepEqual(tree, expected_tree, PropertiesEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(GroupLeafWithPreviousLeafTest, ExtendCommonAncestor) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 1);
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(part1.TokensRange().end() + 1);
UnwrappedLine part3(0, part2.TokensRange().end());
part3.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part2 + part3
UnwrappedLine group_part(0, part2.TokensRange().begin());
group_part.SpanUpToToken(part3.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
part1,
tree_type{part1},
tree_type{part2},
tree_type{part3},
};
const tree_type expected_tree{
all,
tree_type{part1},
tree_type{
group_part,
tree_type{part2},
tree_type{part3},
},
};
auto* group = GroupLeafWithPreviousLeaf(&tree.Children().back());
EXPECT_EQ(group, &tree.Children().back());
const auto diff = DeepEqual(tree, expected_tree, PropertiesEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
static bool TokenRangeEqual(const UnwrappedLine& left,
const UnwrappedLine& right) {
return left.TokensRange() == right.TokensRange();
}
class MergeLeafIntoPreviousLeafTest : public TokenPartitionTreeTestFixture {};
TEST_F(MergeLeafIntoPreviousLeafTest, RootOnly) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
};
const auto saved_tree(tree); // deep copy
auto* parent = MergeLeafIntoPreviousLeaf(&tree);
EXPECT_EQ(parent, nullptr);
// Expect no change.
const auto diff = DeepEqual(tree, saved_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoPreviousLeafTest, OneChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all, tree_type{all}, // subtree spans same range
};
ASSERT_FALSE(is_leaf(tree));
const auto saved_tree(tree); // deep copy
auto* parent = MergeLeafIntoPreviousLeaf(&tree.Children().front());
EXPECT_EQ(parent, nullptr);
// Expect no change.
const auto diff = DeepEqual(tree, saved_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoPreviousLeafTest, TwoChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{part1},
tree_type{part2},
};
const tree_type expected_tree{
all,
tree_type{all},
};
auto* parent = MergeLeafIntoPreviousLeaf(&tree.Children().back());
EXPECT_EQ(parent, &tree);
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoPreviousLeafTest, TwoGenerations) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part1b + part2
UnwrappedLine fused_part(0, part1b.TokensRange().begin());
fused_part.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1, tree_type{part1a}, // unchanged
tree_type{part1b}, // target partition
},
tree_type{part2}, // source partition
};
const tree_type expected_tree{
all,
tree_type{
all, tree_type{part1a},
tree_type{fused_part}, // fused target partition
},
};
auto* parent = MergeLeafIntoPreviousLeaf(&tree.Children().back());
EXPECT_EQ(parent, &tree);
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoPreviousLeafTest, Cousins) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
UnwrappedLine part2a(0, part2.TokensRange().begin());
part2a.SpanUpToToken(part2.TokensRange().begin() + 1);
UnwrappedLine part2b(0, part2a.TokensRange().end());
part2b.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part1b + part2a
UnwrappedLine fused_part(0, part1b.TokensRange().begin());
fused_part.SpanUpToToken(part2a.TokensRange().end());
UnwrappedLine fused_parent(0, begin);
fused_parent.SpanUpToToken(part2a.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1, tree_type{part1a}, // unchanged
tree_type{part1b}, // target partition
},
tree_type{
part2,
tree_type{part2a}, // source partition
tree_type{part2b},
},
};
const tree_type expected_tree{
all,
tree_type{
fused_parent, tree_type{part1a},
tree_type{fused_part}, // fused target partition
},
tree_type{
part2b,
tree_type{part2b},
},
};
auto* parent =
MergeLeafIntoPreviousLeaf(&tree.Children().back().Children().front());
EXPECT_EQ(parent, &tree.Children().back());
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
class MergeLeafIntoNextLeafTest : public TokenPartitionTreeTestFixture {};
TEST_F(MergeLeafIntoNextLeafTest, RootOnly) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
};
const auto saved_tree(tree); // deep copy
auto* parent = MergeLeafIntoNextLeaf(&tree);
EXPECT_EQ(parent, nullptr);
// Expect no change.
const auto diff = DeepEqual(tree, saved_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoNextLeafTest, OneChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all, tree_type{all}, // subtree spans same range
};
ASSERT_FALSE(is_leaf(tree));
const auto saved_tree(tree); // deep copy
auto* parent = MergeLeafIntoNextLeaf(&tree.Children().front());
EXPECT_EQ(parent, nullptr);
// Expect no change.
const auto diff = DeepEqual(tree, saved_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoNextLeafTest, TwoChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{part1},
tree_type{part2},
};
const tree_type expected_tree{
all,
tree_type{all},
};
auto* parent = MergeLeafIntoNextLeaf(&tree.Children().front());
EXPECT_EQ(parent, &tree);
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoNextLeafTest, TwoGenerations) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part1b + part2
UnwrappedLine fused_part(0, part1b.TokensRange().begin());
fused_part.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1, tree_type{part1a}, // unchanged
tree_type{part1b}, // origin partition
},
tree_type{part2}, // target partition
};
const tree_type expected_tree{
all,
tree_type{
part1a,
tree_type{part1a},
},
tree_type{fused_part}, // fused target partition
};
auto* parent =
MergeLeafIntoNextLeaf(&tree.Children().front().Children().back());
EXPECT_EQ(parent, &tree.Children().front());
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeLeafIntoNextLeafTest, Cousins) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
UnwrappedLine part2a(0, part2.TokensRange().begin());
part2a.SpanUpToToken(part2.TokensRange().begin() + 1);
UnwrappedLine part2b(0, part2a.TokensRange().end());
part2b.SpanUpToToken(all.TokensRange().end());
// New expected partition should be part1b + part2a
UnwrappedLine fused_part(0, part1b.TokensRange().begin());
fused_part.SpanUpToToken(part2a.TokensRange().end());
UnwrappedLine fused_parent(0, part1b.TokensRange().begin());
fused_parent.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1, tree_type{part1a}, // unchanged
tree_type{part1b}, // source partition
},
tree_type{
part2,
tree_type{part2a}, // target partition
tree_type{part2b},
},
};
const tree_type expected_tree{
all,
tree_type{
part1a,
tree_type{part1a},
},
tree_type{
fused_parent,
tree_type{fused_part}, // fused target partition
tree_type{part2b},
},
};
auto* parent =
MergeLeafIntoNextLeaf(&tree.Children().front().Children().back());
EXPECT_EQ(parent, &tree.Children().front());
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
class MergeConsecutiveSiblingsTest : public TokenPartitionTreeTestFixture {};
TEST_F(MergeConsecutiveSiblingsTest, OneChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all, tree_type{all}, // subtree spans same range
};
// Need at least 2 sliblings to join.
EXPECT_DEATH(MergeConsecutiveSiblings(&tree, 0), "");
}
TEST_F(MergeConsecutiveSiblingsTest, TwoChild) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{part1},
tree_type{part2},
};
const tree_type expected_tree{
all, tree_type{all}, // concatenated unwrapped line ranges
};
MergeConsecutiveSiblings(&tree, 0);
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
TEST_F(MergeConsecutiveSiblingsTest, TwoGenerations) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
// Construct an artificial tree using the following partitions.
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine part1(0, begin);
part1.SpanUpToToken(begin + 3);
UnwrappedLine part1a(0, begin);
part1a.SpanUpToToken(begin + 2);
UnwrappedLine part1b(0, part1a.TokensRange().end());
part1b.SpanUpToToken(part1.TokensRange().end());
UnwrappedLine part2(0, part1.TokensRange().end());
part2.SpanUpToToken(all.TokensRange().end());
UnwrappedLine part2a(0, begin);
part2a.SpanUpToToken(begin + 1);
UnwrappedLine part2b(0, part1a.TokensRange().end());
part2b.SpanUpToToken(part2.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{
part1,
tree_type{part1a},
tree_type{part1b},
},
tree_type{
part2,
tree_type{part2a},
tree_type{part2b},
},
};
const tree_type expected_tree{
all,
tree_type{
all, // part1 + part2
// children of part1 and part2 are concatenated
tree_type{part1a},
tree_type{part1b},
tree_type{part2a},
tree_type{part2b},
},
};
MergeConsecutiveSiblings(&tree, 0);
const auto diff = DeepEqual(tree, expected_tree, TokenRangeEqual);
EXPECT_TRUE(diff.left == nullptr) << "First differing node at:\n"
<< *diff.left << "\nand:\n"
<< *diff.right << '\n';
}
class AnyPartitionSubRangeIsDisabledTest
: public TokenPartitionTreeTestFixture {};
TEST_F(AnyPartitionSubRangeIsDisabledTest, Various) {
// Construct three partitions, sizes: 1, 3, 2
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition2(0, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
ASSERT_EQ(partition2.TokensRange().size(), 2);
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
{
const TokenPartitionRange empty(tree.Children().begin(),
tree.Children().begin());
EXPECT_FALSE(AnyPartitionSubRangeIsDisabled(empty, joined_token_text_,
ByteOffsetSet{}));
}
{
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
EXPECT_FALSE(AnyPartitionSubRangeIsDisabled(range, joined_token_text_,
ByteOffsetSet{}));
EXPECT_TRUE(AnyPartitionSubRangeIsDisabled(range, joined_token_text_,
ByteOffsetSet{{0, 1}}));
EXPECT_TRUE(AnyPartitionSubRangeIsDisabled(range, joined_token_text_,
ByteOffsetSet{{10, 20}}));
EXPECT_TRUE(AnyPartitionSubRangeIsDisabled(range, joined_token_text_,
ByteOffsetSet{{21, 22}}));
EXPECT_FALSE(AnyPartitionSubRangeIsDisabled(range, joined_token_text_,
ByteOffsetSet{{22, 23}}));
EXPECT_FALSE(AnyPartitionSubRangeIsDisabled(range, joined_token_text_,
ByteOffsetSet{{30, 40}}));
EXPECT_TRUE(AnyPartitionSubRangeIsDisabled(
range, joined_token_text_, ByteOffsetSet{{5, 6}, {30, 40}}));
}
}
class GetSubpartitionsBetweenBlankLinesTest
: public ::testing::Test,
public UnwrappedLineMemoryHandler {
public:
explicit GetSubpartitionsBetweenBlankLinesTest(absl::string_view text)
: sample_(text),
tokens_(
absl::StrSplit(sample_, absl::ByAnyChar(" \n"), absl::SkipEmpty())),
partition_(/* temporary */ UnwrappedLine()) {
CHECK_EQ(tokens_.size(), 8);
for (const auto token : tokens_) {
ftokens_.emplace_back(TokenInfo{1, token});
}
// sample_ is the memory-owning string buffer
CreateTokenInfosExternalStringBuffer(ftokens_);
// Establish format token ranges per partition.
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine child1(0, begin);
child1.SpanUpToToken(begin + 2);
UnwrappedLine child2(0, begin + 2);
child2.SpanUpToToken(begin + 4);
UnwrappedLine child3(0, begin + 4);
child3.SpanUpToToken(begin + 6);
UnwrappedLine child4(0, begin + 6);
child4.SpanUpToToken(begin + 8);
// Construct 2-level token partition.
using tree_type = TokenPartitionTree;
partition_ = tree_type{
all,
tree_type{child1},
tree_type{child2},
tree_type{child3},
tree_type{child4},
};
}
protected:
const std::string sample_;
const std::vector<absl::string_view> tokens_;
std::vector<TokenInfo> ftokens_;
TokenPartitionTree partition_;
};
class GetSubpartitionsNoNewlinesTest
: public GetSubpartitionsBetweenBlankLinesTest {
public:
GetSubpartitionsNoNewlinesTest()
: GetSubpartitionsBetweenBlankLinesTest(
"one two three four five seven six eight") {}
};
TEST_F(GetSubpartitionsNoNewlinesTest, NoNewlines) {
const TokenPartitionRange range(partition_.Children().begin(),
partition_.Children().end());
const auto partition_ranges = GetSubpartitionsBetweenBlankLines(range);
EXPECT_THAT(partition_ranges,
ElementsAre(TokenPartitionRange(range.begin(), range.end())));
}
class GetSubpartitionsNoBlanksTest
: public GetSubpartitionsBetweenBlankLinesTest {
public:
GetSubpartitionsNoBlanksTest()
: GetSubpartitionsBetweenBlankLinesTest(
"one two three\nfour five seven\nsix eight") {}
};
TEST_F(GetSubpartitionsNoBlanksTest, NoBlanks) {
const TokenPartitionRange range(partition_.Children().begin(),
partition_.Children().end());
const auto partition_ranges = GetSubpartitionsBetweenBlankLines(range);
EXPECT_THAT(partition_ranges,
ElementsAre(TokenPartitionRange(range.begin(), range.end())));
}
class GetSubpartitionsWithBlanksTest
: public GetSubpartitionsBetweenBlankLinesTest {
public:
GetSubpartitionsWithBlanksTest()
: GetSubpartitionsBetweenBlankLinesTest(
"one two\n\nthree four five seven\n\nsix eight") {}
};
TEST_F(GetSubpartitionsWithBlanksTest, WithBlanks) {
const TokenPartitionRange range(partition_.Children().begin(),
partition_.Children().end());
const auto partition_ranges = GetSubpartitionsBetweenBlankLines(range);
EXPECT_THAT(
partition_ranges,
ElementsAre(TokenPartitionRange(range.begin(), range.begin() + 1),
TokenPartitionRange(range.begin() + 1, range.begin() + 3),
TokenPartitionRange(range.begin() + 3, range.begin() + 4)));
}
class IndentButPreserveOtherSpacingTest : public TokenPartitionTreeTestFixture {
};
TEST_F(IndentButPreserveOtherSpacingTest, Various) {
// Construct three partitions, sizes: 1, 3, 2
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
UnwrappedLine partition0(0, begin);
partition0.SpanUpToToken(begin + 1);
UnwrappedLine partition1(0, partition0.TokensRange().end());
partition1.SpanUpToToken(begin + 4);
UnwrappedLine partition2(0, partition1.TokensRange().end());
partition2.SpanUpToToken(preformat_tokens.end());
// Construct an artificial tree using the above partitions.
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{partition0},
tree_type{partition1},
tree_type{partition2},
};
const TokenPartitionRange range(tree.Children().begin(),
tree.Children().end());
IndentButPreserveOtherSpacing(range, joined_token_text_, &pre_format_tokens_);
// The first tokens on each partition need not be preserved, but all
// subsequent tokens should be preserved.
for (const auto& child : range) {
auto token_range = child.Value().TokensRange();
if (token_range.empty()) continue;
token_range.pop_front();
for (const auto& ftoken : token_range) {
EXPECT_EQ(ftoken.before.break_decision, SpacingOptions::Preserve);
}
}
}
class ReshapeFittingSubpartitionsTest : public TokenPartitionTreeTestFixture {};
TEST_F(ReshapeFittingSubpartitionsTest, NoArguments) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
using tree_type = TokenPartitionTree;
tree_type tree{
header,
tree_type{header},
};
const tree_type tree_expected{
header,
tree_type{header},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
// Check tree structure
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
TEST_F(ReshapeFittingSubpartitionsTest, OneArgumentInSubpartition) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine all(0, begin);
all.SpanUpToToken(arg1.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
arg1,
tree_type{arg1},
},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
TEST_F(ReshapeFittingSubpartitionsTest, OneArgumentInSubpartitionAndTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine trailer(0, arg1.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
arg1,
tree_type{arg1},
},
tree_type{trailer},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{trailer},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
TEST_F(ReshapeFittingSubpartitionsTest, OneArgumentFlat) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine all(0, begin);
all.SpanUpToToken(arg1.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all, tree_type{header}, tree_type{arg1}, // flat, not in subpartition
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
TEST_F(ReshapeFittingSubpartitionsTest, OneArgumentFlatAndTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine trailer(0, arg1.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{arg1}, // flat, not in subpartition
tree_type{trailer},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{trailer},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
TEST_F(ReshapeFittingSubpartitionsTest, TwoArguments) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(arg2.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
TEST_F(ReshapeFittingSubpartitionsTest, TwoArgumentsAndTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine trailer(0, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
},
tree_type{trailer},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
tree_type{trailer},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
// All fits in one partition
TEST_F(ReshapeFittingSubpartitionsTest, OnePartition) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg5.TokensRange().end());
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(arg5.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
// All fits in one partition
TEST_F(ReshapeFittingSubpartitionsTest, OnePartitionWithTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(0, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{trailer},
},
};
verible::BasicFormatStyle style; // default
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
}
// Wrap into two partitions
TEST_F(ReshapeFittingSubpartitionsTest, TwoPartitions) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg5.TokensRange().end());
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(arg5.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
UnwrappedLine group1(0, header.TokensRange().begin());
group1.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine group2(0, arg3.TokensRange().begin());
group2.SpanUpToToken(arg5.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{
group1,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
},
tree_type{
group2,
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
verible::BasicFormatStyle style; // default
style.column_limit = 14;
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(
tree.Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length()); // indenation equal first token length
}
// Wrap into two partitions
TEST_F(ReshapeFittingSubpartitionsTest, TwoPartitionsWithTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(0, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
UnwrappedLine group1(0, header.TokensRange().begin());
group1.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine group2(0, arg3.TokensRange().begin());
group2.SpanUpToToken(trailer.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{
group1,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
},
tree_type{
group2,
tree_type{arg3},
tree_type{arg4},
tree_type{trailer},
},
};
verible::BasicFormatStyle style; // default
style.column_limit = 14;
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(
tree.Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length()); // indenation equal first token length
}
// None fits
TEST_F(ReshapeFittingSubpartitionsTest, NoneOneFits) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg5.TokensRange().end());
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(arg5.TokensRange().end());
verible::BasicFormatStyle style; // default
style.column_limit = 2;
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
ApplyPreOrder(tree.Children()[1], [&style](TokenPartitionTree& node) {
auto& uwline = node.Value();
uwline.SetIndentationSpaces(style.wrap_spaces);
});
const tree_type tree_expected{
all,
tree_type{header, tree_type{header}},
tree_type{arg1, tree_type{arg1}},
tree_type{arg2, tree_type{arg2}},
tree_type{arg3, tree_type{arg3}},
tree_type{arg4, tree_type{arg4}},
tree_type{arg5, tree_type{arg5}},
};
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[4].Value().IndentationSpaces(), style.wrap_spaces);
}
// None fits
TEST_F(ReshapeFittingSubpartitionsTest, NoneOneFitsWithTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
verible::BasicFormatStyle style; // default
style.column_limit = 2;
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(style.wrap_spaces, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(style.wrap_spaces, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(style.wrap_spaces, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(style.wrap_spaces, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(style.wrap_spaces, arg1.TokensRange().begin());
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(0, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
const tree_type tree_expected{
all,
tree_type{header, tree_type{header}},
tree_type{arg1, tree_type{arg1}},
tree_type{arg2, tree_type{arg2}},
tree_type{arg3, tree_type{arg3}},
tree_type{arg4, tree_type{arg4}},
tree_type{trailer, tree_type{trailer}},
};
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[4].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[5].Value().IndentationSpaces(), 0);
}
// All fits in one partition
TEST_F(ReshapeFittingSubpartitionsTest, IndentationOnePartition) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg5.TokensRange().end());
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(arg5.TokensRange().end());
verible::BasicFormatStyle style; // default
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
ApplyPreOrder(tree.Children()[1], [&style](TokenPartitionTree& node) {
auto& uwline = node.Value();
uwline.SetIndentationSpaces(3 + style.indentation_spaces);
});
tree.Children()[0].Value().SetIndentationSpaces(3);
tree.Value().SetIndentationSpaces(3);
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// keep orig indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(),
saved_tree.Value().IndentationSpaces());
}
// All fits in one partition
TEST_F(ReshapeFittingSubpartitionsTest, IndentationOnePartitionWithTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
verible::BasicFormatStyle style; // default
UnwrappedLine header(3, begin);
header.SpanNextToken();
UnwrappedLine arg1(3 + style.indentation_spaces, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(3 + style.indentation_spaces, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(3 + style.indentation_spaces, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(3 + style.indentation_spaces, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(3 + style.indentation_spaces, arg1.TokensRange().begin());
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(3, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(3, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
const tree_type tree_expected{
all,
tree_type{
all,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{trailer},
},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// keep orig indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(),
saved_tree.Value().IndentationSpaces());
}
// Wrap into two partitions
TEST_F(ReshapeFittingSubpartitionsTest, IndentationTwoPartitions) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg5.TokensRange().end());
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(arg5.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
verible::BasicFormatStyle style; // default
style.column_limit = 14 + 7;
ApplyPreOrder(tree.Children()[1], [&style](TokenPartitionTree& node) {
auto& uwline = node.Value();
uwline.SetIndentationSpaces(7 + style.indentation_spaces);
});
tree.Children()[0].Value().SetIndentationSpaces(7);
tree.Value().SetIndentationSpaces(7);
UnwrappedLine group1(0, header.TokensRange().begin());
group1.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine group2(0, arg3.TokensRange().begin());
group2.SpanUpToToken(arg5.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{
group1,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
},
tree_type{
group2,
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check group nodes indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 7);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
EXPECT_EQ(saved_tree.Children()[0].Value().IndentationSpaces(),
tree.Children()[0].Children()[0].Value().IndentationSpaces());
EXPECT_EQ(tree.Children()[0].Children()[1].Value().IndentationSpaces(), 7);
EXPECT_EQ(tree.Children()[0].Children()[2].Value().IndentationSpaces(), 7);
EXPECT_EQ(tree.Children()[1].Children()[0].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
EXPECT_EQ(tree.Children()[1].Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
EXPECT_EQ(tree.Children()[1].Children()[2].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
}
// Wrap into two partitions
TEST_F(ReshapeFittingSubpartitionsTest, IndentationTwoPartitionsWithTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
verible::BasicFormatStyle style; // default
style.column_limit = 14 + 7;
UnwrappedLine header(7, begin);
header.SpanNextToken();
UnwrappedLine arg1(7 + style.indentation_spaces, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(7 + style.indentation_spaces, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(7 + style.indentation_spaces, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(7 + style.indentation_spaces, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(7 + style.indentation_spaces, arg1.TokensRange().begin());
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(7, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(7, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
UnwrappedLine group1(0, header.TokensRange().begin());
group1.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine group2(0, arg3.TokensRange().begin());
group2.SpanUpToToken(trailer.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{
group1,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
},
tree_type{
group2,
tree_type{arg3},
tree_type{arg4},
tree_type{trailer},
},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check group nodes indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 7);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
EXPECT_EQ(saved_tree.Children()[0].Value().IndentationSpaces(),
tree.Children()[0].Children()[0].Value().IndentationSpaces());
EXPECT_EQ(tree.Children()[0].Children()[1].Value().IndentationSpaces(), 7);
EXPECT_EQ(tree.Children()[0].Children()[2].Value().IndentationSpaces(), 7);
EXPECT_EQ(tree.Children()[1].Children()[0].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
EXPECT_EQ(tree.Children()[1].Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
EXPECT_EQ(tree.Children()[1].Children()[2].Value().IndentationSpaces(),
header.TokensRange()[0].Length() + 7); // appended
}
// One subpartition per line
TEST_F(ReshapeFittingSubpartitionsTest, OnePerLine) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
verible::BasicFormatStyle style; // default
const int kIndent = style.indentation_spaces;
UnwrappedLine header(1 * kIndent, begin);
header.SpanNextToken();
UnwrappedLine arg1(2 * kIndent, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(2 * kIndent, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(2 * kIndent, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(2 * kIndent, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(2 * kIndent, arg1.TokensRange().begin(),
PartitionPolicyEnum::kAlwaysExpand);
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine all(1 * kIndent, header.TokensRange().begin());
all.SpanUpToToken(args.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
};
UnwrappedLine first_line(1 * kIndent, header.TokensRange().begin());
first_line.SpanUpToToken(arg1.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{
first_line,
tree_type{header},
tree_type{arg1},
},
tree_type{arg2, tree_type{arg2}},
tree_type{arg3, tree_type{arg3}},
tree_type{arg4, tree_type{arg4}},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check group nodes indentation
const int header_width = header.TokensRange()[0].Length();
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 1 * kIndent);
EXPECT_EQ(tree.Children()[0].Children()[0].Value().IndentationSpaces(),
1 * kIndent);
EXPECT_EQ(tree.Children()[0].Children()[1].Value().IndentationSpaces(),
1 * kIndent);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[1].Children()[0].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[2].Children()[0].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[3].Children()[0].Value().IndentationSpaces(),
1 * kIndent + header_width);
}
// One subpartition per line
TEST_F(ReshapeFittingSubpartitionsTest, OnePerLineWithTrailer) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
verible::BasicFormatStyle style; // default
const int kIndent = style.indentation_spaces;
UnwrappedLine header(1 * kIndent, begin);
header.SpanNextToken();
UnwrappedLine arg1(2 * kIndent, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(2 * kIndent, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(2 * kIndent, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(2 * kIndent, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(2 * kIndent, arg1.TokensRange().begin(),
PartitionPolicyEnum::kAlwaysExpand);
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(1 * kIndent, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(1 * kIndent, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
UnwrappedLine first_line(1 * kIndent, header.TokensRange().begin());
first_line.SpanUpToToken(arg1.TokensRange().end());
UnwrappedLine last_line(1 * kIndent, arg4.TokensRange().begin());
last_line.SpanUpToToken(trailer.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{
first_line,
tree_type{header},
tree_type{arg1},
},
tree_type{arg2, tree_type{arg2}},
tree_type{arg3, tree_type{arg3}},
tree_type{
last_line,
tree_type{arg4},
tree_type{trailer},
},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check group nodes indentation
const int header_width = header.TokensRange()[0].Length();
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 1 * kIndent);
EXPECT_EQ(tree.Children()[0].Children()[0].Value().IndentationSpaces(),
1 * kIndent);
EXPECT_EQ(tree.Children()[0].Children()[1].Value().IndentationSpaces(),
1 * kIndent);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[1].Children()[0].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[2].Children()[0].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[3].Children()[0].Value().IndentationSpaces(),
1 * kIndent + header_width);
EXPECT_EQ(tree.Children()[3].Children()[1].Value().IndentationSpaces(),
1 * kIndent + header_width);
}
TEST_F(ReshapeFittingSubpartitionsTest, AvoidExceedingColumnLimit) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
verible::BasicFormatStyle style; // default
style.column_limit = 9;
UnwrappedLine header(0, begin);
header.SpanNextToken();
UnwrappedLine arg1(1, header.TokensRange().end());
arg1.SpanNextToken();
UnwrappedLine arg2(1, arg1.TokensRange().end());
arg2.SpanNextToken();
UnwrappedLine arg3(1, arg2.TokensRange().end());
arg3.SpanNextToken();
UnwrappedLine arg4(1, arg3.TokensRange().end());
arg4.SpanNextToken();
UnwrappedLine args(1, arg1.TokensRange().begin());
args.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine trailer(0, args.TokensRange().end());
trailer.SpanNextToken();
UnwrappedLine all(0, header.TokensRange().begin());
all.SpanUpToToken(trailer.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer},
};
UnwrappedLine second_line(1, arg1.TokensRange().begin());
second_line.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine third_line(1, arg3.TokensRange().begin());
third_line.SpanUpToToken(arg4.TokensRange().end());
const tree_type tree_expected{
all,
tree_type{header, tree_type{header}},
tree_type{
second_line,
tree_type{arg1},
tree_type{arg2},
},
tree_type{
third_line,
tree_type{arg3},
tree_type{arg4},
},
tree_type{trailer, tree_type{trailer}},
};
auto saved_tree(tree);
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check group nodes indentation
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[0].Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(), 1);
EXPECT_EQ(tree.Children()[1].Children()[0].Value().IndentationSpaces(), 1);
EXPECT_EQ(tree.Children()[1].Children()[1].Value().IndentationSpaces(), 1);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(), 1);
EXPECT_EQ(tree.Children()[2].Children()[0].Value().IndentationSpaces(), 1);
EXPECT_EQ(tree.Children()[2].Children()[1].Value().IndentationSpaces(), 1);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[3].Children()[0].Value().IndentationSpaces(), 0);
}
// Tests with real-world example
class ReshapeFittingSubpartitionsTestFixture
: public ::testing::Test,
public UnwrappedLineMemoryHandler {
public:
ReshapeFittingSubpartitionsTestFixture()
// combining what would normally be a type and a variable name
// into a single string for testing convenience
: sample_(
"function_fffffffffff( "
"type_a_aaaa, type_b_bbbbb, "
"type_c_cccccc, type_d_dddddddd, "
"type_e_eeeeeeee, type_f_ffff);"),
tokens_(absl::StrSplit(sample_, ' ')) {
for (const auto token : tokens_) {
ftokens_.emplace_back(TokenInfo{1, token});
}
CreateTokenInfos(ftokens_);
}
protected:
const std::string sample_;
const std::vector<absl::string_view> tokens_;
std::vector<TokenInfo> ftokens_;
};
class ReshapeFittingSubpartitionsFunctionTest
: public ReshapeFittingSubpartitionsTestFixture {};
TEST_F(ReshapeFittingSubpartitionsFunctionTest,
FunctionWithSixArgumentsAndColumnLimit20Characters) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// 'function_fffffffffff('
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
// function arguments
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine arg6(0, arg5.TokensRange().end());
arg6.SpanUpToToken(all.TokensRange().end());
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg6.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
tree_type{arg6},
},
};
verible::BasicFormatStyle style;
style.column_limit = 20;
ApplyPreOrder(tree.Children()[1], [&style](TokenPartitionTree& node) {
auto& uwline = node.Value();
uwline.SetIndentationSpaces(style.wrap_spaces);
});
// Expect each subpartition in its own partition...
const tree_type tree_expected{
all,
tree_type{header, tree_type{header}},
tree_type{arg1, tree_type{arg1}},
tree_type{arg2, tree_type{arg2}},
tree_type{arg3, tree_type{arg3}},
tree_type{arg4, tree_type{arg4}},
tree_type{arg5, tree_type{arg5}},
tree_type{arg6, tree_type{arg6}},
};
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentations
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[4].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[5].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[6].Value().IndentationSpaces(), style.wrap_spaces);
}
TEST_F(ReshapeFittingSubpartitionsFunctionTest,
FunctionWithSixArgumentsAndColumnLimit40Characters) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// 'function_fffffffffff('
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
// function arguments
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine arg6(0, arg5.TokensRange().end());
arg6.SpanUpToToken(all.TokensRange().end());
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg6.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
tree_type{arg6},
},
};
verible::BasicFormatStyle style;
style.column_limit = 40;
ApplyPreOrder(tree.Children()[1], [&style](TokenPartitionTree& node) {
auto& uwline = node.Value();
uwline.SetIndentationSpaces(style.wrap_spaces);
});
UnwrappedLine group1(0, begin);
group1.SpanUpToToken(header.TokensRange().end());
UnwrappedLine group2(0, arg1.TokensRange().begin());
group2.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine group3(0, arg3.TokensRange().begin());
group3.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine group4(0, arg5.TokensRange().begin());
group4.SpanUpToToken(arg6.TokensRange().end());
const tree_type tree_expected{all,
tree_type{
group1,
tree_type{header},
},
tree_type{
group2,
tree_type{arg1},
tree_type{arg2},
},
tree_type{
group3,
tree_type{arg3},
tree_type{arg4},
},
tree_type{
group4,
tree_type{arg5},
tree_type{arg6},
}};
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentations
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(), style.wrap_spaces);
EXPECT_EQ(tree.Children()[3].Value().IndentationSpaces(), style.wrap_spaces);
}
TEST_F(ReshapeFittingSubpartitionsFunctionTest,
FunctionWithSixArgumentsAndColumnLimit56Characters) {
const auto& preformat_tokens = pre_format_tokens_;
const auto begin = preformat_tokens.begin();
UnwrappedLine all(0, begin);
all.SpanUpToToken(preformat_tokens.end());
// 'function_fffffffffff('
UnwrappedLine header(0, begin);
header.SpanUpToToken(begin + 1);
// function arguments
UnwrappedLine arg1(0, header.TokensRange().end());
arg1.SpanUpToToken(begin + 2);
UnwrappedLine arg2(0, arg1.TokensRange().end());
arg2.SpanUpToToken(begin + 3);
UnwrappedLine arg3(0, arg2.TokensRange().end());
arg3.SpanUpToToken(begin + 4);
UnwrappedLine arg4(0, arg3.TokensRange().end());
arg4.SpanUpToToken(begin + 5);
UnwrappedLine arg5(0, arg4.TokensRange().end());
arg5.SpanUpToToken(begin + 6);
UnwrappedLine arg6(0, arg5.TokensRange().end());
arg6.SpanUpToToken(all.TokensRange().end());
UnwrappedLine args(0, arg1.TokensRange().begin());
args.SpanUpToToken(arg6.TokensRange().end());
using tree_type = TokenPartitionTree;
tree_type tree{
all,
tree_type{header},
tree_type{
args,
tree_type{arg1},
tree_type{arg2},
tree_type{arg3},
tree_type{arg4},
tree_type{arg5},
tree_type{arg6},
},
};
UnwrappedLine group1(0, begin);
group1.SpanUpToToken(arg2.TokensRange().end());
UnwrappedLine group2(0, arg3.TokensRange().begin());
group2.SpanUpToToken(arg4.TokensRange().end());
UnwrappedLine group3(0, arg5.TokensRange().begin());
group3.SpanUpToToken(arg6.TokensRange().end());
const tree_type tree_expected{all,
tree_type{
group1,
tree_type{header},
tree_type{arg1},
tree_type{arg2},
},
tree_type{
group2,
tree_type{arg3},
tree_type{arg4},
},
tree_type{
group3,
tree_type{arg5},
tree_type{arg6},
}};
verible::BasicFormatStyle style;
style.column_limit = 56;
ReshapeFittingSubpartitions(style, &tree);
const auto diff = DeepEqual(tree, tree_expected, TokenRangeEqual);
EXPECT_EQ(diff.left, nullptr) << "Expected:\n"
<< tree_expected << "\nGot:" << tree << "\n";
// Check indentations (appending)
EXPECT_EQ(tree.Children()[0].Value().IndentationSpaces(), 0);
EXPECT_EQ(tree.Children()[1].Value().IndentationSpaces(),
header.TokensRange()[0].Length());
EXPECT_EQ(tree.Children()[2].Value().IndentationSpaces(),
header.TokensRange()[0].Length());
}
class ApplyAlreadyFormattedPartitionPropertiesToTokensTest
: public TokenPartitionTreeTestFixture {};
TEST_F(ApplyAlreadyFormattedPartitionPropertiesToTokensTest, EmptyTokenRange) {
using TPT = TokenPartitionTreeBuilder;
using SO = SpacingOptions;
auto tree = TPT(3, {0, 0}, PartitionPolicyEnum::kAlreadyFormatted)
.build(pre_format_tokens_);
ApplyAlreadyFormattedPartitionPropertiesToTokens(&tree, &pre_format_tokens_);
static const int expected_spaces[] = {0, 0, 0, 0, 0, 0};
static const SpacingOptions expected_decision[] = {
SO::Undecided, SO::Undecided, SO::Undecided,
SO::Undecided, SO::Undecided, SO::Undecided,
};
for (int i = 0; i < static_cast<int>(pre_format_tokens_.size()); ++i) {
const auto& token = pre_format_tokens_.at(i);
EXPECT_EQ(token.before.spaces_required, expected_spaces[i])
<< "Token " << i;
EXPECT_EQ(token.before.break_decision, expected_decision[i])
<< "Token " << i;
}
}
TEST_F(ApplyAlreadyFormattedPartitionPropertiesToTokensTest, NoInlines) {
using TPT = TokenPartitionTreeBuilder;
using SO = SpacingOptions;
auto tree = TPT(1, {0, 5}, PartitionPolicyEnum::kAlreadyFormatted)
.build(pre_format_tokens_);
ApplyAlreadyFormattedPartitionPropertiesToTokens(&tree, &pre_format_tokens_);
static const int expected_spaces[] = {0, 0, 0, 0, 0, 0};
static const SpacingOptions expected_decision[] = {
SO::MustWrap, SO::MustAppend, SO::MustAppend,
SO::MustAppend, SO::MustAppend, SO::Undecided,
};
for (int i = 0; i < static_cast<int>(pre_format_tokens_.size()); ++i) {
const auto& token = pre_format_tokens_.at(i);
EXPECT_EQ(token.before.spaces_required, expected_spaces[i])
<< "Token " << i;
EXPECT_EQ(token.before.break_decision, expected_decision[i])
<< "Token " << i;
}
}
TEST_F(ApplyAlreadyFormattedPartitionPropertiesToTokensTest, TwoInlines) {
using TPT = TokenPartitionTreeBuilder;
using SO = SpacingOptions;
auto tree = TPT(7, PartitionPolicyEnum::kAlreadyFormatted,
{
TPT(2, {0, 2}, PartitionPolicyEnum::kInline),
TPT(4, {2, 5}, PartitionPolicyEnum::kInline),
})
.build(pre_format_tokens_);
ApplyAlreadyFormattedPartitionPropertiesToTokens(&tree, &pre_format_tokens_);
static const int expected_spaces[] = {2, 0, 4, 0, 0, 0};
static const SpacingOptions expected_decision[] = {
SO::AppendAligned, SO::MustAppend, SO::AppendAligned,
SO::MustAppend, SO::MustAppend, SO::Undecided,
};
for (int i = 0; i < static_cast<int>(pre_format_tokens_.size()); ++i) {
const auto& token = pre_format_tokens_.at(i);
EXPECT_EQ(token.before.spaces_required, expected_spaces[i])
<< "Token " << i;
EXPECT_EQ(token.before.break_decision, expected_decision[i])
<< "Token " << i;
}
}
class TokenPartitionTreesEqualPredFormatTest
: public TokenPartitionTreeTestFixture {};
TEST_F(TokenPartitionTreesEqualPredFormatTest, EqualityCheck) {
using TPT = TokenPartitionTreeBuilder;
static const auto ref_tree =
TPT(PartitionPolicyEnum::kAlwaysExpand,
{
TPT(2, {0, 2}, PartitionPolicyEnum::kFitOnLineElseExpand),
TPT(4, {2, 4}, PartitionPolicyEnum::kUninitialized),
})
.build(pre_format_tokens_);
{
static const auto tree = ref_tree;
const auto result =
TokenPartitionTreesEqualPredFormat("first", "second", ref_tree, tree);
EXPECT_TRUE(result);
EXPECT_STREQ(result.message(), "");
}
{
// Different indentation of the first child
static const auto tree =
TPT(PartitionPolicyEnum::kAlwaysExpand,
{
TPT(3, {0, 2}, PartitionPolicyEnum::kFitOnLineElseExpand),
TPT(4, {2, 4}, PartitionPolicyEnum::kUninitialized),
})
.build(pre_format_tokens_);
const auto result =
TokenPartitionTreesEqualPredFormat("first", "second", ref_tree, tree);
EXPECT_FALSE(result);
EXPECT_STREQ(result.message(),
"Expected equality of these trees:\n"
"Actual:\n"
"{ ([one two three four], policy: always-expand)\n"
" { (>>[one two], policy: fit-else-expand) }\n"
" { (>>>>[three four], policy: uninitialized) }\n"
"}\n"
"Expected:\n"
"{ ([one two three four], policy: always-expand)\n"
" { (>>>[one two], policy: fit-else-expand) }\n"
" { (>>>>[three four], policy: uninitialized) }\n"
"}\n");
}
{
// Different tokens range
static const auto tree =
TPT(PartitionPolicyEnum::kAlwaysExpand,
{
TPT(2, {0, 2}, PartitionPolicyEnum::kFitOnLineElseExpand),
TPT(4, {2, 5}, PartitionPolicyEnum::kUninitialized),
})
.build(pre_format_tokens_);
const auto result =
TokenPartitionTreesEqualPredFormat("first", "second", ref_tree, tree);
EXPECT_FALSE(result);
EXPECT_STREQ(result.message(),
"Expected equality of these trees:\n"
"Actual:\n"
"{ ([one two three four], policy: always-expand)\n"
" { (>>[one two], policy: fit-else-expand) }\n"
" { (>>>>[three four], policy: uninitialized) }\n"
"}\n"
"Expected:\n"
"{ ([one two three four five], policy: always-expand)\n"
" { (>>[one two], policy: fit-else-expand) }\n"
" { (>>>>[three four five], policy: uninitialized) }\n"
"}\n");
}
{
// Different policy
static const auto tree =
TPT(PartitionPolicyEnum::kFitOnLineElseExpand,
{
TPT(2, {0, 2}, PartitionPolicyEnum::kFitOnLineElseExpand),
TPT(4, {2, 4}, PartitionPolicyEnum::kUninitialized),
})
.build(pre_format_tokens_);
const auto result =
TokenPartitionTreesEqualPredFormat("first", "second", ref_tree, tree);
EXPECT_FALSE(result);
EXPECT_STREQ(result.message(),
"Expected equality of these trees:\n"
"Actual:\n"
"{ ([one two three four], policy: always-expand)\n"
" { (>>[one two], policy: fit-else-expand) }\n"
" { (>>>>[three four], policy: uninitialized) }\n"
"}\n"
"Expected:\n"
"{ ([one two three four], policy: fit-else-expand)\n"
" { (>>[one two], policy: fit-else-expand) }\n"
" { (>>>>[three four], policy: uninitialized) }\n"
"}\n");
}
}
} // namespace
} // namespace verible