| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync/model/string_ordinal.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/rand_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/abseil-cpp/absl/strings/ascii.h" |
| |
| namespace syncer { |
| |
| namespace { |
| |
| // Create StringOrdinals that satisfy all but one criterion for validity. |
| // IsValid() should return false for all of them. |
| TEST(StringOrdinalTest, Invalid) { |
| // Length criterion. |
| EXPECT_FALSE(StringOrdinal(std::string()).IsValid()); |
| |
| const char kBeforeA[] = {'a' - 1, '\0'}; |
| const char kAfterZ[] = {'z' + 1, '\0'}; |
| |
| // Character criterion. |
| EXPECT_FALSE(StringOrdinal(kBeforeA).IsValid()); |
| EXPECT_FALSE(StringOrdinal(kAfterZ).IsValid()); |
| |
| // Zero criterion. |
| EXPECT_FALSE(StringOrdinal("a").IsValid()); |
| |
| // Trailing zero criterion. |
| EXPECT_FALSE(StringOrdinal("ba").IsValid()); |
| } |
| |
| // Create StringOrdinals that satisfy all criteria for validity. |
| // IsValid() should return true for all of them. |
| TEST(StringOrdinalTest, Valid) { |
| // Length criterion. |
| EXPECT_TRUE(StringOrdinal("b").IsValid()); |
| } |
| |
| // Create StringOrdinals from CreateInitialOrdinal. They should be valid |
| // and close to the middle of the range. |
| TEST(StringOrdinalTest, CreateInitialOrdinal) { |
| const StringOrdinal& ordinal = StringOrdinal::CreateInitialOrdinal(); |
| ASSERT_TRUE(ordinal.IsValid()); |
| // "n" is the midpoint letter of the alphabet. |
| EXPECT_TRUE(ordinal.Equals(StringOrdinal("n"))); |
| } |
| |
| // Create an invalid and a valid StringOrdinal. EqualsOrBothInvalid should |
| // return true if called reflexively and false otherwise. |
| TEST(StringOrdinalTest, EqualsOrBothInvalid) { |
| const StringOrdinal& valid_ordinal = StringOrdinal::CreateInitialOrdinal(); |
| const StringOrdinal invalid_ordinal; |
| |
| EXPECT_TRUE(valid_ordinal.EqualsOrBothInvalid(valid_ordinal)); |
| EXPECT_TRUE(invalid_ordinal.EqualsOrBothInvalid(invalid_ordinal)); |
| EXPECT_FALSE(invalid_ordinal.EqualsOrBothInvalid(valid_ordinal)); |
| EXPECT_FALSE(valid_ordinal.EqualsOrBothInvalid(invalid_ordinal)); |
| } |
| |
| // Create three StringOrdinals in order. LessThan should return values |
| // consistent with that order. |
| TEST(StringOrdinalTest, LessThan) { |
| const StringOrdinal small_ordinal("b"); |
| const StringOrdinal middle_ordinal("c"); |
| const StringOrdinal big_ordinal("d"); |
| |
| EXPECT_FALSE(small_ordinal.LessThan(small_ordinal)); |
| EXPECT_TRUE(small_ordinal.LessThan(middle_ordinal)); |
| EXPECT_TRUE(small_ordinal.LessThan(big_ordinal)); |
| |
| EXPECT_FALSE(middle_ordinal.LessThan(small_ordinal)); |
| EXPECT_FALSE(middle_ordinal.LessThan(middle_ordinal)); |
| EXPECT_TRUE(middle_ordinal.LessThan(big_ordinal)); |
| |
| EXPECT_FALSE(big_ordinal.LessThan(small_ordinal)); |
| EXPECT_FALSE(big_ordinal.LessThan(middle_ordinal)); |
| EXPECT_FALSE(big_ordinal.LessThan(big_ordinal)); |
| } |
| |
| // Create three StringOrdinals in order. GreaterThan should return values |
| // consistent with that order. |
| TEST(StringOrdinalTest, GreaterThan) { |
| const StringOrdinal small_ordinal("b"); |
| const StringOrdinal middle_ordinal("c"); |
| const StringOrdinal big_ordinal("d"); |
| |
| EXPECT_FALSE(small_ordinal.GreaterThan(small_ordinal)); |
| EXPECT_FALSE(small_ordinal.GreaterThan(middle_ordinal)); |
| EXPECT_FALSE(small_ordinal.GreaterThan(big_ordinal)); |
| |
| EXPECT_TRUE(middle_ordinal.GreaterThan(small_ordinal)); |
| EXPECT_FALSE(middle_ordinal.GreaterThan(middle_ordinal)); |
| EXPECT_FALSE(middle_ordinal.GreaterThan(big_ordinal)); |
| |
| EXPECT_TRUE(big_ordinal.GreaterThan(small_ordinal)); |
| EXPECT_TRUE(big_ordinal.GreaterThan(middle_ordinal)); |
| EXPECT_FALSE(big_ordinal.GreaterThan(big_ordinal)); |
| } |
| |
| // Create two valid StringOrdinals. Equals should return true only when |
| // called reflexively. |
| TEST(StringOrdinalTest, Equals) { |
| const StringOrdinal ordinal1("b"); |
| const StringOrdinal ordinal2("c"); |
| |
| EXPECT_TRUE(ordinal1.Equals(ordinal1)); |
| EXPECT_FALSE(ordinal1.Equals(ordinal2)); |
| |
| EXPECT_FALSE(ordinal2.Equals(ordinal1)); |
| EXPECT_TRUE(ordinal2.Equals(ordinal2)); |
| } |
| |
| // Create some valid ordinals from some byte strings. |
| // ToInternalValue() should return the original byte string. |
| TEST(StringOrdinalTest, ToInternalValue) { |
| EXPECT_EQ("c", StringOrdinal("c").ToInternalValue()); |
| EXPECT_EQ("bcdef", StringOrdinal("bcdef").ToInternalValue()); |
| } |
| |
| bool IsNonEmptyPrintableString(const std::string& str) { |
| if (str.empty()) { |
| return false; |
| } |
| for (char c : str) { |
| if (!absl::ascii_isprint(static_cast<unsigned char>(c))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Create some invalid/valid ordinals. ToDebugString() should always |
| // return a non-empty printable string. |
| TEST(StringOrdinalTest, ToDebugString) { |
| EXPECT_TRUE(IsNonEmptyPrintableString(StringOrdinal().ToDebugString())); |
| EXPECT_TRUE(IsNonEmptyPrintableString( |
| StringOrdinal("invalid string").ToDebugString())); |
| EXPECT_TRUE(IsNonEmptyPrintableString(StringOrdinal("c").ToDebugString())); |
| EXPECT_TRUE( |
| IsNonEmptyPrintableString(StringOrdinal("bcdef").ToDebugString())); |
| } |
| |
| // Create three StringOrdinals in order. LessThanFn should return values |
| // consistent with that order. |
| TEST(StringOrdinalTest, LessThanFn) { |
| const StringOrdinal small_ordinal("b"); |
| const StringOrdinal middle_ordinal("c"); |
| const StringOrdinal big_ordinal("d"); |
| |
| const StringOrdinal::LessThanFn less_than; |
| |
| EXPECT_FALSE(less_than(small_ordinal, small_ordinal)); |
| EXPECT_TRUE(less_than(small_ordinal, middle_ordinal)); |
| EXPECT_TRUE(less_than(small_ordinal, big_ordinal)); |
| |
| EXPECT_FALSE(less_than(middle_ordinal, small_ordinal)); |
| EXPECT_FALSE(less_than(middle_ordinal, middle_ordinal)); |
| EXPECT_TRUE(less_than(middle_ordinal, big_ordinal)); |
| |
| EXPECT_FALSE(less_than(big_ordinal, small_ordinal)); |
| EXPECT_FALSE(less_than(big_ordinal, middle_ordinal)); |
| EXPECT_FALSE(less_than(big_ordinal, big_ordinal)); |
| } |
| |
| std::string GetBetween(const std::string& ordinal_string1, |
| const std::string& ordinal_string2) { |
| const StringOrdinal ordinal1(ordinal_string1); |
| const StringOrdinal ordinal2(ordinal_string2); |
| const StringOrdinal between1 = ordinal1.CreateBetween(ordinal2); |
| const StringOrdinal between2 = ordinal2.CreateBetween(ordinal1); |
| EXPECT_TRUE(between1.Equals(between2)); |
| return between1.ToInternalValue(); |
| } |
| |
| // Create some StringOrdinals from single-digit strings. Given two strings |
| // from this set, CreateBetween should return a StringOrdinal roughly between |
| // them that are also single-digit when possible. |
| TEST(StringOrdinalTest, CreateBetweenSingleDigit) { |
| EXPECT_EQ("c", GetBetween("b", "d")); |
| EXPECT_EQ("bn", GetBetween("b", "c")); |
| EXPECT_EQ("cn", GetBetween("c", "d")); |
| } |
| |
| // Create some StringOrdinals from strings of various lengths. Given two |
| // strings from this set, CreateBetween should return an StringOrdinal roughly |
| // between them that have as few digits as possible. |
| TEST(StringOrdinalTest, CreateBetweenDifferentLengths) { |
| EXPECT_EQ("ban", GetBetween("b", "bb")); |
| EXPECT_EQ("c", GetBetween("b", "db")); |
| EXPECT_EQ("bo", GetBetween("bd", "c")); |
| EXPECT_EQ("c", GetBetween("baaab", "d")); |
| EXPECT_EQ("c", GetBetween("baaac", "d")); |
| EXPECT_EQ("c", GetBetween("b", "daaac")); |
| } |
| |
| // Create some StringOrdinals specifically designed to trigger overflow |
| // cases. Given two strings from this set, CreateBetween should |
| // return a StringOrdinal roughly between them that have as few digits as |
| // possible. |
| TEST(StringOrdinalTest, CreateBetweenOverflow) { |
| EXPECT_EQ("ao", GetBetween("ab", "bb")); |
| EXPECT_EQ("bo", GetBetween("bb", "cb")); |
| EXPECT_EQ("bbo", GetBetween("bbb", "bcb")); |
| EXPECT_EQ("bo", GetBetween("aab", "ddd")); |
| EXPECT_EQ("cpp", GetBetween("ccc", "ddd")); |
| EXPECT_EQ("co", GetBetween("cab", "ddd")); |
| EXPECT_EQ("bo", GetBetween("aad", "ddd")); |
| EXPECT_EQ("boo", GetBetween("cccd", "bbbd")); |
| } |
| |
| // Create some StringOrdinals. CreateBefore should return a StringOrdinal |
| // roughly halfway towards "a". |
| TEST(StringOrdinalTest, CreateBefore) { |
| EXPECT_EQ("an", StringOrdinal("b").CreateBefore().ToInternalValue()); |
| EXPECT_EQ("ao", StringOrdinal("bb").CreateBefore().ToInternalValue()); |
| EXPECT_EQ("ao", StringOrdinal("bc").CreateBefore().ToInternalValue()); |
| EXPECT_EQ("ap", StringOrdinal("bd").CreateBefore().ToInternalValue()); |
| } |
| |
| // Create some StringOrdinals. CreateAfter should return a StringOrdinal |
| // roughly halfway towards "a". |
| TEST(StringOrdinalTest, CreateAfter) { |
| EXPECT_EQ("o", StringOrdinal("d").CreateAfter().ToInternalValue()); |
| EXPECT_EQ("on", StringOrdinal("dc").CreateAfter().ToInternalValue()); |
| EXPECT_EQ("ooon", StringOrdinal("dddc").CreateAfter().ToInternalValue()); |
| EXPECT_EQ("o", StringOrdinal("cc").CreateAfter().ToInternalValue()); |
| EXPECT_EQ("o", StringOrdinal("cd").CreateAfter().ToInternalValue()); |
| } |
| |
| // Create two valid StringOrdinals. EqualsFn should return true only when |
| // called reflexively. |
| TEST(StringOrdinalTest, EqualsFn) { |
| const StringOrdinal ordinal1("b"); |
| const StringOrdinal ordinal2("c"); |
| |
| const StringOrdinal::EqualsFn equals; |
| |
| EXPECT_TRUE(equals(ordinal1, ordinal1)); |
| EXPECT_FALSE(equals(ordinal1, ordinal2)); |
| |
| EXPECT_FALSE(equals(ordinal2, ordinal1)); |
| EXPECT_TRUE(equals(ordinal2, ordinal2)); |
| } |
| |
| // Create some StringOrdinals and shuffle them. Sorting them using |
| // LessThanFn should produce the correct order. |
| TEST(StringOrdinalTest, Sort) { |
| const StringOrdinal ordinal1("bcefg"); |
| const StringOrdinal ordinal2("gfecb"); |
| const StringOrdinal ordinal3("ihgfe"); |
| const StringOrdinal ordinal4("jihgf"); |
| |
| std::vector<StringOrdinal> sorted_ordinals; |
| sorted_ordinals.push_back(ordinal1); |
| sorted_ordinals.push_back(ordinal2); |
| sorted_ordinals.push_back(ordinal3); |
| sorted_ordinals.push_back(ordinal4); |
| |
| std::vector<StringOrdinal> ordinals = sorted_ordinals; |
| base::RandomShuffle(ordinals.begin(), ordinals.end()); |
| std::ranges::sort(ordinals, StringOrdinal::LessThanFn()); |
| EXPECT_TRUE( |
| std::ranges::equal(ordinals, sorted_ordinals, StringOrdinal::EqualsFn())); |
| } |
| |
| } // namespace |
| |
| } // namespace syncer |