summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/llchatservicehistorycore.cpp7
-rw-r--r--indra/newview/tests/llchatservicehistorycore_test.cpp54
2 files changed, 55 insertions, 6 deletions
diff --git a/indra/newview/llchatservicehistorycore.cpp b/indra/newview/llchatservicehistorycore.cpp
index 44fe43b608..deeba807b4 100644
--- a/indra/newview/llchatservicehistorycore.cpp
+++ b/indra/newview/llchatservicehistorycore.cpp
@@ -375,7 +375,8 @@ std::list<LLSD> filterDirectHistoryDuplicates(
const std::list<LLSD>& history, const std::list<LLSD>& live)
{
// Reserve known send times before receipt intervals and local-clock minutes.
- // Within each kind, the earliest delivery takes the earliest eligible occurrence.
+ // Within each kind, match newest deliveries to newest eligible occurrences so
+ // earlier repeated messages retain their historical position.
auto priority = [](const LLSD& message)
{
const std::string source = message["chat_service_time_source"].asString();
@@ -386,7 +387,7 @@ std::list<LLSD> filterDirectHistoryDuplicates(
std::stable_sort(deliveries.begin(), deliveries.end(), [&](const LLSD* a, const LLSD* b)
{
return priority(*a) != priority(*b) ? priority(*a) < priority(*b)
- : (*a)["timestamp"].asInteger() < (*b)["timestamp"].asInteger();
+ : (*a)["timestamp"].asInteger() > (*b)["timestamp"].asInteger();
});
// Sorted iterators allow matching in time order while retaining the source order.
@@ -398,7 +399,7 @@ std::list<LLSD> filterDirectHistoryDuplicates(
}
std::stable_sort(service.begin(), service.end(), [](auto a, auto b)
{
- return (*a)["timestamp"].asInteger() < (*b)["timestamp"].asInteger();
+ return (*a)["timestamp"].asInteger() > (*b)["timestamp"].asInteger();
});
std::vector<bool> matched(deliveries.size(), false);
for (size_t pos = 0; pos < deliveries.size(); ++pos)
diff --git a/indra/newview/tests/llchatservicehistorycore_test.cpp b/indra/newview/tests/llchatservicehistorycore_test.cpp
index 6316d5b7bc..81a87eef84 100644
--- a/indra/newview/tests/llchatservicehistorycore_test.cpp
+++ b/indra/newview/tests/llchatservicehistorycore_test.cpp
@@ -664,15 +664,15 @@ template<> template<> void object_t::test<26>()
LLSD delivered = repeatedDelivery(1);
delivered["chat_service_time_source"] = "receipt";
- // Receipt times do not identify a particular repeated send. Allocate the earliest
- // eligible occurrence, preserving the other occurrence even at the same second.
+ // Prefer the most recent eligible occurrence when a receipt can match repeated
+ // sends, preserving the earlier occurrence in history.
for (bool reversed : {false, true})
{
std::list<LLSD> history{earlier, later};
if (reversed) history.reverse();
const auto filtered = filterDirectHistoryDuplicates(history, {delivered});
ensure_equals("one live delivery consumes one service occurrence", filtered.size(), size_t(1));
- ensure_equals("the later repeated send survives", filtered.front()["chat_service_msg_id"].asString(), std::string(SECOND));
+ ensure_equals("the earlier repeated send survives", filtered.front()["chat_service_msg_id"].asString(), std::string(FIRST));
}
}
@@ -949,4 +949,52 @@ template<> template<> void object_t::test<34>()
ensure_equals("source window order preserves counts", mergeDirectHistory(right, left).size(), size_t(3));
}
+template<> template<> void object_t::test<35>()
+{
+ // Recreating a session leaves only the latest delivery live. An offline repeat
+ // within its receipt window must retain its earlier place in the transcript.
+ LLSD earlier = contextRow(1);
+ earlier["message"] = "hello";
+ earlier["timestamp"] = 1789092403;
+ LLSD between = contextRow(2);
+ between["message"] = "6";
+ between["timestamp"] = 1789092414;
+ LLSD later = contextRow(3);
+ later["message"] = "hello";
+ later["timestamp"] = 1789092446;
+ const Messages archive{earlier, between, later};
+
+ for (S32 delay : {0, 2, 15})
+ {
+ History history;
+ history.setLoaded({earlier, between});
+ LLSD delivery = liveCopy(later);
+ delivery["chat_service_time_source"] = "receipt";
+ delivery["timestamp"] = later["timestamp"].asInteger() + delay;
+ Messages current{delivery};
+
+ // The archive read can complete before the latest service head arrives.
+ replaceHistory(current, history.compose({}, {delivery}, 10, true), true);
+ for (int publication = 0; publication < 3; ++publication)
+ {
+ const auto composed = history.compose(archive, {delivery}, 10, true);
+ ensure_equals("one delivery replaces only one historical occurrence", composed.size(), size_t(2));
+ ensure_equals("the earlier hello retains its identity and position",
+ composed.front()["chat_service_msg_id"].asString(), earlier["chat_service_msg_id"].asString());
+ const bool changed = replaceHistory(current, composed, true);
+ ensure_equals("unchanged publications do not replay the transcript", changed, publication == 0);
+ ensure_equals("both hellos and the intervening message remain", current.size(), size_t(3));
+ }
+
+ // Reopening with all rows historical preserves the same message order.
+ Messages reopened;
+ replaceHistory(reopened, archive, true);
+ auto expected = reopened.begin();
+ for (const LLSD& row : current)
+ {
+ ensure_equals("reopening preserves message order", row["message"].asString(), (*expected++)["message"].asString());
+ }
+ }
+}
+
}