blob: 4b4efab324ff966cf8f4cbe5c73eb9594280b84a [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 "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "common/formatting/format_token.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/vector_tree.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 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(tree.Children().empty());
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(tree.Children().empty());
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 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 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(&tree, style);
// 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(&tree, style);
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(&tree, style);
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(&tree, style);
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(&tree, style);
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(&tree, style);
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());
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{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}},
};
verible::BasicFormatStyle style; // default
style.column_limit = 2;
ReshapeFittingSubpartitions(&tree, style);
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);
}
// 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},
},
};
tree.Children()[1].ApplyPreOrder([&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(&tree, style);
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;
tree.Children()[1].ApplyPreOrder([&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(&tree, style);
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
}
// 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},
},
};
// 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}},
};
verible::BasicFormatStyle style;
style.column_limit = 20;
ReshapeFittingSubpartitions(&tree, style);
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},
},
};
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},
}};
verible::BasicFormatStyle style;
style.column_limit = 40;
ReshapeFittingSubpartitions(&tree, style);
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(&tree, style);
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());
}
} // namespace
} // namespace verible