Skip to content

Commit 469c8a5

Browse files
FrayxRulezlevlam
authored andcommitted
Add tests for OrderedTopics.
1 parent ada8b8b commit 469c8a5

2 files changed

Lines changed: 153 additions & 0 deletions

File tree

‎test/CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ set(TD_TEST_SOURCE
99
${CMAKE_CURRENT_SOURCE_DIR}/link.cpp
1010
${CMAKE_CURRENT_SOURCE_DIR}/message_entities.cpp
1111
${CMAKE_CURRENT_SOURCE_DIR}/mtproto.cpp
12+
${CMAKE_CURRENT_SOURCE_DIR}/ordered_topics.cpp
1213
${CMAKE_CURRENT_SOURCE_DIR}/poll.cpp
1314
${CMAKE_CURRENT_SOURCE_DIR}/query_merger.cpp
1415
${CMAKE_CURRENT_SOURCE_DIR}/requested_dialog_type.cpp

‎test/ordered_topics.cpp‎

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//
2+
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2026
3+
//
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
#include "td/telegram/OrderedTopics.h"
8+
9+
#include "td/utils/algorithm.h"
10+
#include "td/utils/common.h"
11+
#include "td/utils/FlatHashMap.h"
12+
#include "td/utils/Random.h"
13+
#include "td/utils/tests.h"
14+
15+
#include <algorithm>
16+
#include <limits>
17+
18+
namespace {
19+
20+
class TestTopicId {
21+
td::int64 id_ = 0;
22+
23+
public:
24+
TestTopicId() = default;
25+
26+
explicit TestTopicId(td::int64 id) : id_(id) {
27+
}
28+
29+
td::int64 get_unique_id() const {
30+
return id_;
31+
}
32+
33+
bool operator==(const TestTopicId &other) const {
34+
return id_ == other.id_;
35+
}
36+
};
37+
38+
// the list as it is documented: topics sorted by order, then by identifier, both descending;
39+
// a topic is visible if it isn't after the last loaded one
40+
class NaiveOrderedTopics {
41+
public:
42+
void update_order(td::int64 topic_id, td::int64 new_order) {
43+
if (new_order == 0) {
44+
orders_.erase(topic_id);
45+
} else {
46+
orders_[topic_id] = new_order;
47+
}
48+
}
49+
50+
void set_last_topic_date(td::int64 order, td::int64 topic_id) {
51+
if (is_after(last_order_, last_topic_id_, order, topic_id)) {
52+
last_order_ = order;
53+
last_topic_id_ = topic_id;
54+
}
55+
}
56+
57+
td::int64 get_public_order(td::int64 order, td::int64 topic_id) const {
58+
return order != 0 && !is_after(last_order_, last_topic_id_, order, topic_id) ? order : 0;
59+
}
60+
61+
std::size_t size() const {
62+
return orders_.size();
63+
}
64+
65+
private:
66+
// returns true, if the topic (order, topic_id) is after the topic (last_order, last_topic_id) in the list
67+
static bool is_after(td::int64 last_order, td::int64 last_topic_id, td::int64 order, td::int64 topic_id) {
68+
return order < last_order || (order == last_order && topic_id < last_topic_id);
69+
}
70+
71+
td::FlatHashMap<td::int64, td::int64> orders_;
72+
td::int64 last_order_ = std::numeric_limits<td::int64>::max();
73+
td::int64 last_topic_id_ = 0;
74+
};
75+
76+
} // namespace
77+
78+
TEST(OrderedTopics, stress) {
79+
td::Random::Xorshift128plus rnd(123);
80+
for (int test = 0; test < 100; test++) {
81+
td::OrderedTopics<TestTopicId> topics;
82+
NaiveOrderedTopics naive;
83+
td::FlatHashMap<td::int64, td::int64> orders;
84+
auto max_topic_id = rnd.fast(1, 50);
85+
auto max_order = rnd.fast(1, 1000);
86+
87+
auto check = [&] {
88+
for (td::int64 topic_id = 1; topic_id <= max_topic_id; topic_id++) {
89+
auto it = orders.find(topic_id);
90+
auto order = it == orders.end() ? 0 : it->second;
91+
ASSERT_EQ(naive.get_public_order(order, topic_id), topics.get_public_order(order, TestTopicId(topic_id)));
92+
}
93+
};
94+
95+
for (int step = 0; step < 1000; step++) {
96+
td::int64 topic_id = rnd.fast(1, static_cast<int>(max_topic_id));
97+
if (rnd.fast(0, 9) == 0) {
98+
td::int64 order = rnd.fast(0, static_cast<int>(max_order));
99+
auto changed_topic_ids = topics.set_last_topic_date({order, TestTopicId(topic_id)});
100+
td::vector<td::int64> expected_topic_ids;
101+
for (auto &it : orders) {
102+
if (naive.get_public_order(it.second, it.first) == 0) {
103+
expected_topic_ids.push_back(it.first);
104+
}
105+
}
106+
naive.set_last_topic_date(order, topic_id);
107+
td::remove_if(expected_topic_ids, [&](td::int64 id) { return naive.get_public_order(orders[id], id) == 0; });
108+
auto changed = td::transform(changed_topic_ids, [](TestTopicId id) { return id.get_unique_id(); });
109+
std::sort(changed.begin(), changed.end());
110+
std::sort(expected_topic_ids.begin(), expected_topic_ids.end());
111+
ASSERT_TRUE(changed == expected_topic_ids);
112+
} else {
113+
td::int64 new_order = rnd.fast(0, 2) == 0 ? 0 : rnd.fast(1, static_cast<int>(max_order));
114+
auto &order = orders[topic_id];
115+
topics.update_order(TestTopicId(topic_id), order, new_order);
116+
naive.update_order(topic_id, new_order);
117+
order = new_order;
118+
if (order == 0) {
119+
orders.erase(topic_id);
120+
}
121+
}
122+
check();
123+
}
124+
}
125+
}
126+
127+
TEST(OrderedTopics, total_count) {
128+
td::OrderedTopics<TestTopicId> topics;
129+
ASSERT_EQ(-1, topics.update_total_count());
130+
131+
topics.update_order(TestTopicId(1), 0, 10);
132+
topics.update_order(TestTopicId(2), 0, 20);
133+
ASSERT_EQ(-1, topics.update_total_count());
134+
135+
// the server knows about more topics than were loaded
136+
topics.set_server_total_count(5);
137+
topics.set_last_topic_date({10, TestTopicId(1)});
138+
ASSERT_EQ(5, topics.update_total_count());
139+
140+
topics.update_order(TestTopicId(3), 0, 30);
141+
ASSERT_EQ(6, topics.get_server_total_count());
142+
ASSERT_EQ(6, topics.update_total_count());
143+
144+
topics.update_order(TestTopicId(3), 30, 0);
145+
ASSERT_EQ(5, topics.get_server_total_count());
146+
147+
// once the whole list is loaded, the local count wins
148+
topics.set_last_topic_date(td::OrderedTopics<TestTopicId>::max_topic_date());
149+
ASSERT_TRUE(topics.is_fully_loaded());
150+
ASSERT_EQ(2, topics.update_total_count());
151+
ASSERT_EQ(2, topics.get_server_total_count());
152+
}

0 commit comments

Comments
 (0)