Pass basic topicTree testcase
This commit is contained in:
+70
-10
@@ -28,15 +28,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
struct Hole {
|
#include <functional>
|
||||||
std::pair<size_t, size_t> lengths;
|
|
||||||
unsigned int messageId;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Intersection {
|
|
||||||
std::pair<std::string, std::string> dataChannels;
|
|
||||||
std::vector<Hole> holes;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace uWS {
|
namespace uWS {
|
||||||
|
|
||||||
@@ -74,6 +66,74 @@ struct Topic {
|
|||||||
std::set<Subscriber *> subs;
|
std::set<Subscriber *> subs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Hole {
|
||||||
|
std::pair<size_t, size_t> lengths;
|
||||||
|
unsigned int messageId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Intersection {
|
||||||
|
std::pair<std::string, std::string> dataChannels;
|
||||||
|
std::vector<Hole> holes;
|
||||||
|
|
||||||
|
void forSubscriber(Subscriber *s, std::vector<unsigned int> &senderForMessages, std::function<void(std::pair<std::string_view, std::string_view>)> cb) {
|
||||||
|
/* How far we already emitted of the two dataChannels */
|
||||||
|
std::pair<size_t, size_t> emitted = {};
|
||||||
|
|
||||||
|
//std::cout << "Subscriber: " << s << std::endl;
|
||||||
|
|
||||||
|
/* Holes are global to the entire topic tree, so we are not guaranteed to find
|
||||||
|
* holes in this intersection - they are sorted, though */
|
||||||
|
int examinedHoles = 0;
|
||||||
|
|
||||||
|
/* This is a slow path of sorts, most subscribers will be observers, not active senders */
|
||||||
|
for (unsigned int id : senderForMessages) {
|
||||||
|
//std::cout << "We are sender for id: " << id << std::endl;
|
||||||
|
|
||||||
|
std::pair<size_t, size_t> toEmit = {};
|
||||||
|
std::pair<size_t, size_t> toIgnore = {};
|
||||||
|
|
||||||
|
/* This linear search is most probably very small - it could be made log2 if every hole
|
||||||
|
* knows about its previous accumulated length, which is easy to set up. However this
|
||||||
|
* log2 search will most likely never be a warranted perf. gain */
|
||||||
|
for (; examinedHoles < holes.size(); examinedHoles++) {
|
||||||
|
if (holes[examinedHoles].messageId == id) {
|
||||||
|
toIgnore.first += holes[examinedHoles].lengths.first;
|
||||||
|
toIgnore.second += holes[examinedHoles].lengths.second;
|
||||||
|
examinedHoles++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* We are not the sender of this message so we should emit it in this segment */
|
||||||
|
toEmit.first += holes[examinedHoles].lengths.first;
|
||||||
|
toEmit.second += holes[examinedHoles].lengths.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit this segment */
|
||||||
|
if (toEmit.first || toEmit.second) {
|
||||||
|
std::pair<std::string_view, std::string_view> cutDataChannels = {
|
||||||
|
std::string_view(dataChannels.first.data() + emitted.first, toEmit.first),
|
||||||
|
std::string_view(dataChannels.second.data() + emitted.second, toEmit.second),
|
||||||
|
};
|
||||||
|
|
||||||
|
cb(cutDataChannels);
|
||||||
|
}
|
||||||
|
|
||||||
|
emitted.first += toEmit.first + toIgnore.first;
|
||||||
|
emitted.second += toEmit.second + toIgnore.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emitted.first == dataChannels.first.length() && emitted.second == dataChannels.second.length()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string_view, std::string_view> cutDataChannels = {
|
||||||
|
std::string_view(dataChannels.first.data() + emitted.first, dataChannels.first.length() - emitted.first),
|
||||||
|
std::string_view(dataChannels.second.data() + emitted.second, dataChannels.second.length() - emitted.second),
|
||||||
|
};
|
||||||
|
|
||||||
|
cb(cutDataChannels);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct TopicTree {
|
struct TopicTree {
|
||||||
|
|
||||||
/* Sender holes */
|
/* Sender holes */
|
||||||
@@ -439,7 +499,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
for (auto &p : complete) {
|
for (auto &p : complete) {
|
||||||
printf("messageId = %d\n", p.first);
|
//printf("messageId = %d\n", p.first);
|
||||||
|
|
||||||
|
|
||||||
res.dataChannels.first.append(p.second.first);
|
res.dataChannels.first.append(p.second.first);
|
||||||
|
|||||||
+50
-99
@@ -3,23 +3,26 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void testUnsubscribeInside() {
|
/* Modifying the topicTree inside callback is not allowed, we had
|
||||||
std::cout << "TestUnsubscribeInside" << std::endl;
|
* tests for this before but we never need this to work anyways.
|
||||||
|
* Closing a socket when reaching too much backpressure is done
|
||||||
|
* deferred to next event loop iteration so we never need to modify
|
||||||
|
* topicTree inside callback - removed this test */
|
||||||
|
|
||||||
|
/* This tests pretty much all features for obvious incorrectness */
|
||||||
|
void testCorrectness() {
|
||||||
|
std::cout << "TestCorrectness" << std::endl;
|
||||||
|
|
||||||
uWS::TopicTree *topicTree;
|
uWS::TopicTree *topicTree;
|
||||||
std::map<void *, std::pair<std::string, std::string>> expectedResult;
|
std::map<void *, std::pair<std::string, std::string>> expectedResult;
|
||||||
|
std::map<void *, std::pair<std::string, std::string>> actualResult;
|
||||||
|
|
||||||
topicTree = new uWS::TopicTree([&topicTree, &expectedResult](uWS::Subscriber *s, Intersection &intersection) {
|
topicTree = new uWS::TopicTree([&topicTree, &actualResult](uWS::Subscriber *s, uWS::Intersection &intersection) {
|
||||||
std::string_view data = intersection.dataChannels.second;
|
|
||||||
|
|
||||||
/* Check for unexpected subscribers */
|
intersection.forSubscriber(s, topicTree->senderHoles[s], [s, &actualResult](std::pair<std::string_view, std::string_view> dataChannels) {
|
||||||
assert(expectedResult.find(s) != expectedResult.end());
|
actualResult[s].first += dataChannels.first;
|
||||||
|
actualResult[s].second += dataChannels.second;
|
||||||
/* Check for unexpected data */
|
});
|
||||||
assert(expectedResult[s].first == intersection.dataChannels.first && expectedResult[s].second == intersection.dataChannels.second);
|
|
||||||
|
|
||||||
/* This one causes mess-up */
|
|
||||||
topicTree->unsubscribeAll(s);
|
|
||||||
|
|
||||||
/* We actually don't use this one */
|
/* We actually don't use this one */
|
||||||
return 0;
|
return 0;
|
||||||
@@ -28,110 +31,60 @@ void testUnsubscribeInside() {
|
|||||||
uWS::Subscriber *s1 = new uWS::Subscriber(nullptr);
|
uWS::Subscriber *s1 = new uWS::Subscriber(nullptr);
|
||||||
uWS::Subscriber *s2 = new uWS::Subscriber(nullptr);
|
uWS::Subscriber *s2 = new uWS::Subscriber(nullptr);
|
||||||
|
|
||||||
/* Fill out expectedResult */
|
/* Make sure s1 < s2 (for debugging) */
|
||||||
expectedResult = {
|
|
||||||
{s1, {"Ett!", "Ett!"}},
|
|
||||||
{s2, {"Två!", "Två!"}}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Make sure s1 < s2 */
|
|
||||||
if (s2 < s1) {
|
if (s2 < s1) {
|
||||||
uWS::Subscriber *tmp = s1;
|
uWS::Subscriber *tmp = s1;
|
||||||
s1 = s2;
|
s1 = s2;
|
||||||
s2 = tmp;
|
s2 = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This order does not matter as it fills a tree */
|
/* Publish to topic3 - nobody should see this */
|
||||||
topicTree->subscribe("2", s2);
|
topicTree->publish("topic3", {std::string_view("Nobody"), std::string_view("should see")}, nullptr);
|
||||||
topicTree->subscribe("1", s1);
|
|
||||||
|
|
||||||
/* This order matters, as it fills triggeredTopics array in order */
|
/* Subscribe s1 to topic3 - s1 should not see above message */
|
||||||
topicTree->publish("1", {std::string_view("Ett!"), std::string_view("Ett!")});
|
topicTree->subscribe("topic3", s1);
|
||||||
topicTree->publish("2", {std::string_view("Två!"), std::string_view("Ett!")});
|
|
||||||
|
|
||||||
topicTree->drain();
|
/* Publish to topic3 with s1 as sender - s1 should not get its own messages */
|
||||||
|
topicTree->publish("topic3", {std::string_view("Nobody"), std::string_view("should see")}, s1);
|
||||||
|
|
||||||
/* Release resources */
|
/* Subscribe s2 to topic3 - should not get any message */
|
||||||
topicTree->unsubscribeAll(s1);
|
topicTree->subscribe("topic3", s2);
|
||||||
topicTree->unsubscribeAll(s2);
|
|
||||||
|
|
||||||
delete s1;
|
/* Publish to topic3 without sender - both should see */
|
||||||
delete s2;
|
topicTree->publish("topic3", {std::string_view("Both"), std::string_view("should see")}, nullptr);
|
||||||
|
|
||||||
delete topicTree;
|
/* Publish to topic3 with s2 as sender - s1 should see */
|
||||||
}
|
topicTree->publish("topic3", {std::string_view("s1"), std::string_view("should see, not s2")}, s2);
|
||||||
|
|
||||||
void testPublisherHoles() {
|
/* Publish to topic3 with s1 as sender - s2 should see */
|
||||||
std::cout << "TestPublisherHoles" << std::endl;
|
topicTree->publish("topic3", {std::string_view("s2"), std::string_view("should see, not s1")}, s1);
|
||||||
|
|
||||||
uWS::TopicTree *topicTree;
|
/* Publish to topic3 without sender - both should see */
|
||||||
std::map<void *, std::pair<std::string, std::string>> expectedResult;
|
topicTree->publish("topic3", {std::string_view("Again, both"), std::string_view("should see this as well")}, nullptr);
|
||||||
|
|
||||||
topicTree = new uWS::TopicTree([&topicTree, &expectedResult](uWS::Subscriber *s, /*std::pair<std::string, std::string> &dataChannels*/ Intersection &intersection) {
|
// todo: add more cases involving more topics and duplicates, etc
|
||||||
|
|
||||||
|
|
||||||
std::cout << "Subscriber: " << s << std::endl;
|
|
||||||
|
|
||||||
std::vector<unsigned int> &senderForMessages = topicTree->senderHoles[s];
|
|
||||||
|
|
||||||
for (unsigned int id : senderForMessages) {
|
|
||||||
std::cout << "We are sender for id: " << id << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate holes
|
|
||||||
for (Hole h : intersection.holes) {
|
|
||||||
std::cout << h.messageId << std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// todo: this linear search is not needed as ids are ordered!
|
|
||||||
if (std::find(senderForMessages.begin(), senderForMessages.end(), h.messageId) != senderForMessages.end()) {
|
|
||||||
std::cout << "WE ARE SENDER FOR THIS MESSAGE!" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Check for unexpected subscribers */
|
|
||||||
assert(expectedResult.find(s) != expectedResult.end());
|
|
||||||
|
|
||||||
/* Check for unexpected data */
|
|
||||||
assert(expectedResult[s].first == intersection.dataChannels.first && expectedResult[s].second == intersection.dataChannels.second);
|
|
||||||
|
|
||||||
/* We actually don't use this one */
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
uWS::Subscriber *s1 = new uWS::Subscriber(nullptr);
|
|
||||||
uWS::Subscriber *s2 = new uWS::Subscriber(nullptr);
|
|
||||||
|
|
||||||
/* Fill out expectedResult */
|
/* Fill out expectedResult */
|
||||||
expectedResult = {
|
expectedResult = {
|
||||||
{s1, {"Två!Två!", "Två!Två!"}}, // todo: this one should not receive what he sent himself
|
{s1, {"Boths1Again, both", "should seeshould see, not s2should see this as well"}},
|
||||||
{s2, {"Två!Två!", "Två!Två!"}}
|
{s2, {"Boths2Again, both", "should seeshould see, not s1should see this as well"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Make sure s1 < s2 */
|
/* Compare result with expected result for every subscriber */
|
||||||
if (s2 < s1) {
|
|
||||||
uWS::Subscriber *tmp = s1;
|
|
||||||
s1 = s2;
|
|
||||||
s2 = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This order does not matter as it fills a tree */
|
|
||||||
topicTree->subscribe("1", s2);
|
|
||||||
topicTree->subscribe("1", s1);
|
|
||||||
|
|
||||||
/* This order matters, as it fills triggeredTopics array in order */
|
|
||||||
//topicTree->publish("1", {std::string_view("Ett!"), std::string_view("Ett!")});
|
|
||||||
topicTree->publish("1", {std::string_view("Två!"), std::string_view("Två!")}, s1);
|
|
||||||
topicTree->publish("1", {std::string_view("Två!"), std::string_view("Två!")}, s1);
|
|
||||||
|
|
||||||
topicTree->drain();
|
topicTree->drain();
|
||||||
|
for (auto &p : expectedResult) {
|
||||||
|
std::cout << "Subscriber: " << p.first << std::endl;
|
||||||
|
|
||||||
|
if (p.second.first != actualResult[p.first].first) {
|
||||||
|
std::cout << "ERROR: <" << actualResult[p.first].first << "> should be <" << p.second.first << ">" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.second.second != actualResult[p.first].second) {
|
||||||
|
std::cout << "ERROR: <" << actualResult[p.first].second << "> should be <" << p.second.second << ">" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Release resources */
|
/* Release resources */
|
||||||
topicTree->unsubscribeAll(s1);
|
topicTree->unsubscribeAll(s1);
|
||||||
@@ -144,7 +97,5 @@ void testPublisherHoles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
//testUnsubscribeInside();
|
testCorrectness();
|
||||||
|
|
||||||
testPublisherHoles();
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user