diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index f010f0e961..4d13c459ec 100644 --- a/indra/llcommon/lltracerecording.cpp +++ b/indra/llcommon/lltracerecording.cpp @@ -117,6 +117,17 @@ void Recording::handleReset() { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; #if LL_TRACE_ENABLED + // A running recording that is the newest on its thread gathers in a + // buffer of its own, which update() leaves where it is, and every read + // adds in. Brought up to date first -- what it holds handed down to the + // recordings beneath this one, and into this one's own, which the reset + // then clears -- or a reset while running would keep it. + if (isStarted() && mActiveBuffers && LLTrace::get_thread_recorder()) + { + AccumulatorBufferGroup* buffers = mBuffers.write(); + LLTrace::get_thread_recorder()->deactivate(buffers); + mActiveBuffers = LLTrace::get_thread_recorder()->activate(buffers); + } mBuffers.write()->reset(); mElapsedSeconds = F64Seconds(0.0); diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index 00685d3b1a..3f7a84d3e0 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -88,8 +88,14 @@ namespace } }; + // what drink_coffee() has raised the level to + static F32 sCaffeineLevel(0.f); + struct trace { + // Each case drinks from nothing, whichever ran before it. + trace() { sCaffeineLevel = 0.f; } + // before mRecorder, so it is put back after mRecorder is gone ThreadRecorderRestore mRestore; ThreadRecorder mRecorder; @@ -99,7 +105,6 @@ namespace static SampleStatHandle sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient"); static EventStatHandle sOuncesPerCup("cupsize", "Large, huge, or ginormous"); - static F32 sCaffeineLevel(0.f); const F32Milligrams sCaffeinePerOz(18.f); static void drink_coffee(S32 num_cups, S32Ounces cup_size) @@ -182,4 +187,30 @@ namespace } } + // The newest recording running on a thread gathers in a buffer of its + // own until something brings it up to date. Restarted while it runs, it + // starts from nothing all the same, and what it had gathered still + // reaches the older recording running beneath it. + TEST_CASE_METHOD(trace, "LLTrace: a running recording restarted starts from nothing and the one beneath keeps all", "[LLTrace]") + { + Recording beneath; + Recording newest; + beneath.start(); + newest.start(); + drink_coffee(2, S32GrandeCup(1)); + newest.restart(); + { + INFO("nothing counted once restarted"); + CHECK(newest.getSum(sCupsOfCoffeeConsumed) == 0); + CHECK(newest.getSum(sOuncesPerCup) == S32Ounces(0)); + } + drink_coffee(1, S32TallCup(1)); + UNSCOPED_INFO("only what came after the restart"); + CHECK(newest.getSum(sCupsOfCoffeeConsumed) == 1); + UNSCOPED_INFO("the one beneath kept what came before it too"); + CHECK(beneath.getSum(sCupsOfCoffeeConsumed) == 3); + newest.stop(); + beneath.stop(); + } + } diff --git a/indra/newview/alfeedback.cpp b/indra/newview/alfeedback.cpp index e05c47997c..1e6fa66547 100644 --- a/indra/newview/alfeedback.cpp +++ b/indra/newview/alfeedback.cpp @@ -109,8 +109,13 @@ std::optional ALFeedback::endpointFromDsn(std::string_view return std::nullopt; } - const size_t at = rest.find('@'); - if (at == std::string_view::npos) + // The key is the address's user part, which comes before the host, in + // what ends at the first '/': an '@' after it is the path's. One user + // part: a second would make what follows it the host the address names, + // whatever the host below was taken to be. + const std::string_view authority = rest.substr(0, rest.find('/')); + const size_t at = authority.find('@'); + if (at == std::string_view::npos || authority.find('@', at + 1) != std::string_view::npos) { return std::nullopt; } diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 011b8afd73..853505c855 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -273,13 +273,19 @@ void LLViewerAssetStats::getStat(LLTrace::Recording& rec, T& req, LLViewerAssetS || rec.getSampleCount(sDequeued[cat]) || rec.getSampleCount(sResponse[cat])) { + // With nothing answered, or nothing fetched, there is nothing to take + // the least, the most or the mean of: zeros, rather than what an + // empty minimum, maximum and mean come out as (the largest float, + // the smallest, and NaN). + const S32 responses = rec.getSampleCount(sResponse[cat]); + const S32 fetches = rec.getSampleCount(sBytesFetched[cat]); req .enqueued(rec.getSampleCount(sEnqueued[cat])) .dequeued(rec.getSampleCount(sDequeued[cat])) - .resp_count(rec.getSampleCount(sResponse[cat])) - .resp_min(rec.getMin(sResponse[cat]).value()) - .resp_max(rec.getMax(sResponse[cat]).value()) - .resp_mean(rec.getMean(sResponse[cat]).value()) - .resp_mean_bytes(rec.getMean(sBytesFetched[cat])); + .resp_count(responses) + .resp_min(responses ? rec.getMin(sResponse[cat]).value() : 0.0) + .resp_max(responses ? rec.getMax(sResponse[cat]).value() : 0.0) + .resp_mean(responses ? rec.getMean(sResponse[cat]).value() : 0.0) + .resp_mean_bytes(fetches ? rec.getMean(sBytesFetched[cat]) : 0.0); } } diff --git a/indra/newview/tests/alagentlocationformat_test.cpp b/indra/newview/tests/alagentlocationformat_test.cpp index 3f35a050b8..078660c839 100644 --- a/indra/newview/tests/alagentlocationformat_test.cpp +++ b/indra/newview/tests/alagentlocationformat_test.cpp @@ -202,9 +202,21 @@ namespace TEST_CASE_METHOD(alagentlocationformat_data, "ALAgentLocationFormat: only LANDMARK truncates a long parcel name", "[ALAgentLocationFormat]") { const std::string long_name(200, 'y'); - ALAgentLocationFormat::format(out, LLAgentUI::LOCATION_FORMAT_NORMAL, - long_name, "Region", "", 0, 0, 0); + const auto shown = [&](LLAgentUI::ELocationFormat format) { + ALAgentLocationFormat::format(out, format, long_name, "Region", "General", 0, 0, 0); + return out; + }; UNSCOPED_INFO("NORMAL does not truncate"); - CHECK(out == long_name + ", Region"); + CHECK(shown(LLAgentUI::LOCATION_FORMAT_NORMAL) == long_name + ", Region"); + UNSCOPED_INFO("NORMAL_COORDS does not truncate"); + CHECK(shown(LLAgentUI::LOCATION_FORMAT_NORMAL_COORDS) == long_name + " (0, 0, 0)"); + UNSCOPED_INFO("NO_MATURITY does not truncate"); + CHECK(shown(LLAgentUI::LOCATION_FORMAT_NO_MATURITY) == long_name + ", Region (0, 0, 0)"); + UNSCOPED_INFO("NO_COORDS does not truncate"); + CHECK(shown(LLAgentUI::LOCATION_FORMAT_NO_COORDS) == long_name + ", Region - General"); + UNSCOPED_INFO("FULL does not truncate"); + CHECK(shown(LLAgentUI::LOCATION_FORMAT_FULL) == long_name + ", Region (0, 0, 0) - General"); + UNSCOPED_INFO("LANDMARK does"); + CHECK(shown(LLAgentUI::LOCATION_FORMAT_LANDMARK) == std::string(ALAgentLocationFormat::LANDMARK_NAME_MAX_BYTES, 'y')); } } diff --git a/indra/newview/tests/alcrashreporter_test.cpp b/indra/newview/tests/alcrashreporter_test.cpp index ba248bff05..ad5d0d26c1 100644 --- a/indra/newview/tests/alcrashreporter_test.cpp +++ b/indra/newview/tests/alcrashreporter_test.cpp @@ -121,15 +121,18 @@ namespace const LLVector3 position(128.4f, -63.6f, 22.5f); char buffer[64]; size_t length = ALCrashReporter::locationTag(buffer, sizeof(buffer), "Hippotropolis", position); + REQUIRE(length < sizeof(buffer)); CHECK(std::string(buffer, length) == "Hippotropolis/128/-64/23"); CHECK(buffer[length] == '\0'); char small[12]; length = ALCrashReporter::locationTag(small, sizeof(small), "Hippotropolis", position); + REQUIRE(length < sizeof(small)); CHECK(std::string(small, length) == "Hippotropol"); CHECK(small[length] == '\0'); length = ALCrashReporter::locationTag(small, sizeof(small), "Ahern", position); + REQUIRE(length < sizeof(small)); UNSCOPED_INFO("cut in the numbers"); CHECK(std::string(small, length) == "Ahern/128/-"); } diff --git a/indra/newview/tests/alcurvemodel_test.cpp b/indra/newview/tests/alcurvemodel_test.cpp index dbabd9c9ad..953bc84282 100644 --- a/indra/newview/tests/alcurvemodel_test.cpp +++ b/indra/newview/tests/alcurvemodel_test.cpp @@ -163,6 +163,7 @@ namespace mCurve.movePoint(1, 0.95f, 0.5f); const auto& p = mCurve.getPoints(); + REQUIRE(p.size() == size_t(4)); UNSCOPED_INFO("clamped below its right neighbour"); CHECK(p[1].mX < p[2].mX); UNSCOPED_INFO("clamped to exactly the gap"); @@ -882,6 +883,7 @@ namespace const S32 n = ALToneCurveSet::LUT_SIZE; std::vector texels; set.bake(texels, n); + REQUIRE((S32)texels.size() == n * 4); std::vector xs; for (S32 i = 0; i <= 4096; ++i) diff --git a/indra/newview/tests/aldaycyclelandmarks_test.cpp b/indra/newview/tests/aldaycyclelandmarks_test.cpp index 8dc2b48b34..28c930c042 100644 --- a/indra/newview/tests/aldaycyclelandmarks_test.cpp +++ b/indra/newview/tests/aldaycyclelandmarks_test.cpp @@ -129,7 +129,8 @@ namespace } // Landmarks are cycle positions, so they stay inside [0, 1) even when the - // crossing they came from sits across the seam. + // crossing they came from sits across the seam. Each is found first: an + // absent one is left at 0, which is inside the cycle whatever happened. TEST_CASE_METHOD(daycycle_landmarks, "ALDayCycleLandmarks: landmarks stay inside the cycle when a crossing is across the seam", "[ALDayCycleLandmarks]") { for (S32 i = 0; i < 20; ++i) @@ -138,6 +139,10 @@ namespace const auto marks = ALDayCycleLandmarks::find(sine(phase)); CAPTURE(phase); + REQUIRE(marks.has_sunrise); + REQUIRE(marks.has_noon); + REQUIRE(marks.has_sunset); + REQUIRE(marks.has_midnight); REQUIRE(marks.sunrise >= 0.f); REQUIRE(marks.sunrise < 1.f); REQUIRE(marks.noon >= 0.f); diff --git a/indra/newview/tests/alfarplane_test.cpp b/indra/newview/tests/alfarplane_test.cpp index 404027be15..f14475154d 100644 --- a/indra/newview/tests/alfarplane_test.cpp +++ b/indra/newview/tests/alfarplane_test.cpp @@ -354,7 +354,12 @@ namespace REQUIRE(funcs.find("abs(rel_pos.y)") == std::string::npos); UNSCOPED_INFO("the rotation is in the Environment block"); REQUIRE(shader_source("class1/deferred/environmentBlock.glsl").find("mat3 eyeToSky;") != std::string::npos); + // Read, and calling the haze with the position it unprojected, so the turn's absence below is not the + // absence of the file. + const std::string haze = shader_source("class3/deferred/hazeF.glsl"); UNSCOPED_INFO("the haze pass hands over eye space"); - REQUIRE(shader_source("class3/deferred/hazeF.glsl").find("eyeToSky") == std::string::npos); + REQUIRE(haze.find("calcAtmosphericVarsLinear(pos.xyz,") != std::string::npos); + UNSCOPED_INFO("and does not turn it itself"); + REQUIRE(haze.find("eyeToSky") == std::string::npos); } } diff --git a/indra/newview/tests/alfeedback_test.cpp b/indra/newview/tests/alfeedback_test.cpp index 938904eb43..5ed16472bb 100644 --- a/indra/newview/tests/alfeedback_test.cpp +++ b/indra/newview/tests/alfeedback_test.cpp @@ -77,6 +77,16 @@ namespace CHECK(endpoint->publicKey == "key"); } + // A path may hold an '@': it is the path's, after the host, and not a + // user part. + TEST_CASE("alfeedback: an @ in a self-hosted DSN's path stays in its path", "[alfeedback]") + { + const auto endpoint = ALFeedback::endpointFromDsn("https://k@sentry.example.com/team@blue/7"); + REQUIRE(endpoint.has_value()); + CHECK(endpoint->url == "https://sentry.example.com/team@blue/api/7/envelope/"); + CHECK(endpoint->publicKey == "k"); + } + TEST_CASE("alfeedback: a DSN missing a part is refused", "[alfeedback]") { UNSCOPED_INFO("no scheme"); @@ -85,6 +95,8 @@ namespace REQUIRE_FALSE(ALFeedback::endpointFromDsn("ftp://key@host/1")); UNSCOPED_INFO("no key"); REQUIRE_FALSE(ALFeedback::endpointFromDsn("https://host/1")); + UNSCOPED_INFO("no key, and an @ in the path, which names no user"); + REQUIRE_FALSE(ALFeedback::endpointFromDsn("https://sentry.example.com/team@blue/7")); UNSCOPED_INFO("empty key"); REQUIRE_FALSE(ALFeedback::endpointFromDsn("https://@host/1")); UNSCOPED_INFO("no project"); @@ -393,7 +405,17 @@ namespace UNSCOPED_INFO("a host that starts like loopback"); REQUIRE_FALSE(ALFeedback::endpointFromDsn("http://k@127.0.0.1.example.com/1")); UNSCOPED_INFO("loopback as a user name"); + REQUIRE_FALSE(ALFeedback::endpointFromDsn("http://127.0.0.1@evil.example.com/1")); + UNSCOPED_INFO("loopback in the path"); REQUIRE_FALSE(ALFeedback::endpointFromDsn("http://k@evil.example.com/127.0.0.1/1")); + // A second user part, after the key's, would make what follows it the + // host the address names, whatever the loopback check took for one. + UNSCOPED_INFO("loopback as the user name of another host"); + REQUIRE_FALSE(ALFeedback::endpointFromDsn("http://k@127.0.0.1:8765@evil.example.com/1")); + UNSCOPED_INFO("IPv6 loopback as the user name of another host"); + REQUIRE_FALSE(ALFeedback::endpointFromDsn("http://k@[::1]@evil.example.com/1")); + UNSCOPED_INFO("a second user part, encrypted or not"); + REQUIRE_FALSE(ALFeedback::endpointFromDsn("https://k@o42.ingest.sentry.io@evil.example.com/4501")); } TEST_CASE("alfeedback: a report's logs hide capability addresses and the given words", "[alfeedback]") diff --git a/indra/newview/tests/algradehistory_test.cpp b/indra/newview/tests/algradehistory_test.cpp index a903e326fb..d7ff9ebbaa 100644 --- a/indra/newview/tests/algradehistory_test.cpp +++ b/indra/newview/tests/algradehistory_test.cpp @@ -71,6 +71,7 @@ namespace const auto* t = mHistory.undo(); REQUIRE(t != nullptr); + REQUIRE(t->size() == (size_t)1); UNSCOPED_INFO("from where the drag began"); CHECK(t->front().mBefore.asReal() == 0.0); UNSCOPED_INFO("to where it ended"); @@ -153,6 +154,7 @@ namespace const auto* t = mHistory.redo(); REQUIRE(t != nullptr); + REQUIRE(t->size() == (size_t)1); CHECK(t->front().mAfter.asReal() == 1.0); CHECK_FALSE(mHistory.canRedo()); CHECK(mHistory.canUndo()); @@ -172,6 +174,7 @@ namespace CHECK(mHistory.depth() == (size_t)1); const auto* t = mHistory.undo(); REQUIRE(t != nullptr); + REQUIRE(t->size() == (size_t)1); UNSCOPED_INFO("carrying the new values"); CHECK(t->front().mBefore.asReal() == 1.0); } @@ -189,6 +192,7 @@ namespace // The newest is still the newest after the drop. const auto* t = mHistory.undo(); REQUIRE(t != nullptr); + REQUIRE(t->size() == (size_t)1); UNSCOPED_INFO("which is the last one recorded"); CHECK(t->front().mAfter.asReal() == (F64)(ALGradeHistory::MAX_DEPTH + 20)); } @@ -297,14 +301,15 @@ namespace UNSCOPED_INFO("the group's step carries its name"); CHECK(mHistory.labelOf(0) == std::string("Reset Bloom")); UNSCOPED_INFO("a lone write's step has none"); - REQUIRE(mHistory.labelOf(1).empty()); + CHECK(mHistory.labelOf(1).empty()); // And a group that follows an unnamed one is not named after it. mHistory.beginGroup(); mHistory.record("D", LLSD(0.0), LLSD(1.0), 9.f); mHistory.endGroup(); + REQUIRE(mHistory.depth() == (size_t)3); UNSCOPED_INFO("an unnamed group stays unnamed"); - REQUIRE(mHistory.labelOf(2).empty()); + CHECK(mHistory.labelOf(2).empty()); } // Groups nest, and the one the user started is the one the step is named @@ -398,6 +403,7 @@ namespace CHECK(mHistory.depth() == (size_t)1); const auto* t = mHistory.undo(); REQUIRE(t != nullptr); + REQUIRE(t->size() == (size_t)1); UNSCOPED_INFO("from the value the drag began at"); CHECK(t->front().mBefore.asReal() == 0.0); CHECK(t->front().mAfter.asReal() == 2.0); @@ -410,6 +416,7 @@ namespace mHistory.record("A", LLSD(1.0), LLSD(0.0), 5.1f); UNSCOPED_INFO("the earlier step stays"); REQUIRE(mHistory.depth() == (size_t)1); + REQUIRE(mHistory.at(0).size() == (size_t)1); CHECK(mHistory.at(0).front().mName == std::string("B")); } @@ -445,7 +452,7 @@ namespace UNSCOPED_INFO("a write moves it"); CHECK(recorded != start); - mHistory.depth(); + REQUIRE(mHistory.depth() == (size_t)1); mHistory.cursor(); mHistory.at(0); mHistory.labelOf(0); @@ -476,7 +483,9 @@ namespace mHistory.record("A", LLSD(0.0), LLSD(1.0), 1.f); mHistory.record("B", LLSD(2.0), LLSD(3.0), 5.f); + REQUIRE(mHistory.depth() == (size_t)2); const ALGradeHistory::Transaction shown = mHistory.at(1); + REQUIRE(shown.size() == (size_t)1); const auto* undone = mHistory.undo(); REQUIRE(undone != nullptr); REQUIRE(undone->size() == shown.size()); @@ -500,12 +509,14 @@ namespace } mHistory.undo(); mHistory.undo(); + REQUIRE(mHistory.depth() == (size_t)3); UNSCOPED_INFO("in force"); CHECK(mHistory.labelOf(0) == std::string("one")); UNSCOPED_INFO("the next redo"); CHECK(mHistory.labelOf(1) == std::string("two")); UNSCOPED_INFO("the last redo"); CHECK(mHistory.labelOf(2) == std::string("three")); + REQUIRE(mHistory.at(2).size() == (size_t)1); UNSCOPED_INFO("a redo step's changes"); CHECK(mHistory.at(2).front().mName == std::string("three")); diff --git a/indra/newview/tests/alhuddepth_gl_test.cpp b/indra/newview/tests/alhuddepth_gl_test.cpp index 63f453baa7..877c1e3208 100644 --- a/indra/newview/tests/alhuddepth_gl_test.cpp +++ b/indra/newview/tests/alhuddepth_gl_test.cpp @@ -164,7 +164,6 @@ namespace void setUp(bool reverse) { tearDown(); - mReverse = reverse; setConvention(reverse); std::map defines; @@ -396,7 +395,6 @@ namespace LLMatrix4a mSceneProjection; LLMatrix4a mHUDProjection; std::vector mRects; - bool mReverse = false; }; @@ -601,8 +599,26 @@ namespace blit(); const std::vector before = depths(mWindow); clearUnderHUD(&unloaded); + const std::vector after = depths(mWindow); + // Counted, and the first that differs placed, rather than every depth printed. + size_t differing = 0; + S32 first_x = -1; + S32 first_y = -1; + for (size_t i = 0; i < after.size(); ++i) + { + if (after[i] != before[i]) + { + if (differing == 0) + { + first_x = (S32)(i % W); + first_y = (S32)(i / W); + } + ++differing; + } + } + CAPTURE(first_x, first_y); UNSCOPED_INFO("nothing cleared"); - REQUIRE(depths(mWindow) == before); + REQUIRE(differing == size_t(0)); drawHUD(); restoreUnderHUD(&unloaded); overlay(40, 24, 8.f); diff --git a/indra/newview/tests/alnotecardembedded_test.cpp b/indra/newview/tests/alnotecardembedded_test.cpp index 907946fce4..5b5831e23d 100644 --- a/indra/newview/tests/alnotecardembedded_test.cpp +++ b/indra/newview/tests/alnotecardembedded_test.cpp @@ -188,8 +188,11 @@ namespace { const auto a = item("A"), b = item("B"), c = item("C"); ALNotecardEmbedded& card = make("one " + at(2) + " two " + at(0) + "\n", { a, b, c }); - UNSCOPED_INFO("a button over each item the text stands"); - REQUIRE(view->atomAt(ALTextPos(0, 4)) != nullptr); + { + INFO("a button over each item the text stands"); + CHECK(view->atomAt(ALTextPos(0, 4)) != nullptr); + CHECK(view->atomAt(ALTextPos(0, 13)) != nullptr); + } std::string text; ALNotecardEmbedded::items_t items; card.forSave(text, items); @@ -226,8 +229,9 @@ namespace UNSCOPED_INFO("a new frame's drop lands where the pointer is"); CHECK(view->document().line(where.line).substr(where.column, 4) == at(2)); REQUIRE(tab->items().size() == size_t(3)); + view->undo(); UNSCOPED_INFO("one undo takes the last away"); - CHECK(view->undoJournal().canUndo()); + CHECK(view->document().line(where.line).substr(where.column, 8) == at(0) + at(1)); } TEST_CASE_METHOD(alnotecardembedded_data, "ALNotecardEmbedded: a drop of what a notecard may not carry is refused and says why", "[ALNotecardEmbedded]") @@ -332,7 +336,9 @@ namespace REQUIRE(back[1].isNull()); { INFO("the rest as they were"); + REQUIRE(back[0].notNull()); CHECK(back[0]->getUUID() == a->getUUID()); + REQUIRE(back[2].notNull()); CHECK(back[2]->getName() == "B"); } } diff --git a/indra/newview/tests/alrecoverykeeper_test.cpp b/indra/newview/tests/alrecoverykeeper_test.cpp index c08fe7f254..8372893390 100644 --- a/indra/newview/tests/alrecoverykeeper_test.cpp +++ b/indra/newview/tests/alrecoverykeeper_test.cpp @@ -115,17 +115,19 @@ namespace keeper.pump(3, 10.0 + ALRecoveryKeeper::DELAY); { INFO("a moment after it first changed, whatever was typed meanwhile"); - REQUIRE(mine()); - CHECK(mine()->text == "typed"); - CHECK(mine()->name == "Card"); + const std::optional typed = mine(); + REQUIRE(typed); + CHECK(typed->text == "typed"); + CHECK(typed->name == "Card"); } text = "typed more"; keeper.pump(4, 12.0); keeper.pump(4, 12.0 + ALRecoveryKeeper::DELAY); { INFO("again for the next change"); - REQUIRE(mine()); - CHECK(mine()->text == "typed more"); + const std::optional again = mine(); + REQUIRE(again); + CHECK(again->text == "typed more"); } unsaved = false; keeper.pump(4, 20.0); @@ -171,8 +173,9 @@ namespace keeper.took(*offered, 7); { INFO("the window's own written"); - REQUIRE(mine()); - CHECK(mine()->text == "left behind"); + const std::optional own = mine(); + REQUIRE(own); + CHECK(own->text == "left behind"); } UNSCOPED_INFO("and the one taken up gone"); CHECK_FALSE(keeper.left()); @@ -184,7 +187,7 @@ namespace left.key = keeper.key(); left.item = item; left.text = "not wanted"; - before.write(left); + CHECK(before.write(left)); } offered = keeper.left(); REQUIRE(offered); @@ -192,6 +195,15 @@ namespace keeper.turnDown(*offered); UNSCOPED_INFO("turned down: offered no more"); CHECK_FALSE(keeper.left()); + // Among the discarded as the session that left it: told apart so from + // this session's own, set aside above. + bool turned_down = false; + for (const ALRecoveryEntry& entry : store->list()) + { + turned_down |= entry.state == ALRecoveryEntry::State::Discarded && entry.key == keeper.key() && + entry.session == "another-session"; + } + CHECK(turned_down); } TEST_CASE_METHOD(alrecoverykeeper_data, "ALRecoveryKeeper: a new item or none moves the key and a failed write is said once", "[ALRecoveryKeeper]") diff --git a/indra/newview/tests/alscopedata_test.cpp b/indra/newview/tests/alscopedata_test.cpp index 523b9af84c..c018677d6a 100644 --- a/indra/newview/tests/alscopedata_test.cpp +++ b/indra/newview/tests/alscopedata_test.cpp @@ -17,6 +17,7 @@ #include #include "altestmatchers.h" +#include "../alcolorwheelmodel.h" #include "../alscopedata.h" #include @@ -120,8 +121,18 @@ namespace CHECK_THAT(mScope.getBin(ALScopeData::CH_RED, 64), ll_test::WithinBits(1.f, 5)); CHECK_THAT(mScope.getBin(ALScopeData::CH_GREEN, 128), ll_test::WithinBits(1.f, 5)); CHECK_THAT(mScope.getBin(ALScopeData::CH_BLUE, 192), ll_test::WithinBits(1.f, 5)); - UNSCOPED_INFO("nothing anywhere else"); - CHECK_THAT(mScope.getBin(ALScopeData::CH_RED, 65), ll_test::WithinBits(0.f, 6)); + + // Nothing anywhere else: one bin lit in each channel, luma's too. + for (S32 c = 0; c < ALScopeData::CH_COUNT; ++c) + { + S32 lit = 0; + for (S32 i = 0; i < ALScopeData::BIN_COUNT; ++i) + { + lit += mScope.getBin((ALScopeData::EChannel)c, i) > 0.f ? 1 : 0; + } + CAPTURE(c); + CHECK(lit == 1); + } } // Bins are shares of the sample, so they sum to one whatever the size. @@ -447,7 +458,7 @@ namespace // Cell centres run -1 to 1 in units of MAX_CHROMA, and the binning is the // inverse of that mapping -- so a plot laid out from chromaCellCentre puts // the trace where accumulate() put it, not half a cell away. - TEST_CASE_METHOD(scope_data, "ALScopeData: chroma cell centres rise from -1 to 1 and straddle zero", "[ALScopeData]") + TEST_CASE_METHOD(scope_data, "ALScopeData: chroma cell centres rise from -1 to 1 and the binning is their inverse", "[ALScopeData]") { const S32 last = ALScopeData::CHROMA_SIZE - 1; UNSCOPED_INFO("first cell is at the low edge"); @@ -463,6 +474,25 @@ namespace CAPTURE(i); REQUIRE(ALScopeData::chromaCellCentre(i) > ALScopeData::chromaCellCentre(i - 1)); } + + // The inverse: a colour lands in the cell whose centre is within half + // a cell of its chroma. None of these sits on a cell's edge. + struct Colour + { + S32 r, g, b; + }; + const F32 half_cell = 1.f / (F32)ALScopeData::CHROMA_SIZE; + for (const Colour& c : { Colour{ 210, 40, 70 }, Colour{ 30, 160, 90 }, Colour{ 60, 80, 220 }, Colour{ 180, 150, 40 } }) + { + mScope.accumulate(flat((U8)c.r, (U8)c.g, (U8)c.b, 4).data(), 4, 1); + S32 u = -1, v = -1; + soleCell(mScope, u, v); + F32 cu = 0.f, cv = 0.f; + ALColorWheelModel::toChroma(LLVector3((F32)c.r / 255.f, (F32)c.g / 255.f, (F32)c.b / 255.f), cu, cv); + CAPTURE(c.r, c.g, c.b, u, v); + CHECK_THAT(cu / ALColorWheelModel::MAX_CHROMA, ll_test::WithinDistance(ALScopeData::chromaCellCentre(u), half_cell)); + CHECK_THAT(cv / ALColorWheelModel::MAX_CHROMA, ll_test::WithinDistance(ALScopeData::chromaCellCentre(v), half_cell)); + } } // The whole reason the waveform exists: a histogram cannot tell a blown sky diff --git a/indra/newview/tests/alscriptanalyzers_test.cpp b/indra/newview/tests/alscriptanalyzers_test.cpp index 5e380e0503..70d54baf19 100644 --- a/indra/newview/tests/alscriptanalyzers_test.cpp +++ b/indra/newview/tests/alscriptanalyzers_test.cpp @@ -72,6 +72,11 @@ namespace lsl.answer(shaped, *shaped.text, setup, lsl_shape); UNSCOPED_INFO("LSL: the function, the state, its event"); REQUIRE(lsl_shape.outline.size() == size_t(3)); + { + INFO("the function first"); + CHECK(lsl_shape.outline[0].kind == ALScriptSymbolKind::Function); + CHECK(lsl_shape.outline[0].name == "go"); + } { INFO("the event in its state"); CHECK(lsl_shape.outline[1].kind == ALScriptSymbolKind::State); diff --git a/indra/newview/tests/alscriptcontentsindex_test.cpp b/indra/newview/tests/alscriptcontentsindex_test.cpp index 31c5289d54..8e5dd0e956 100644 --- a/indra/newview/tests/alscriptcontentsindex_test.cpp +++ b/indra/newview/tests/alscriptcontentsindex_test.cpp @@ -157,10 +157,10 @@ namespace CHECK(asked.size() == size_t(1)); CHECK_FALSE(index.fetched(chair)); CHECK(index.prim(chair) == nullptr); - // Unanswered, the prim would not be known, and it is read below. - REQUIRE(answer(chair, { "sit.lsl", "readme" }, true, "Chair")); + CHECK(answer(chair, { "sit.lsl", "readme" }, true, "Chair")); CHECK(heard == 1); CHECK(names(chair) == std::string("sit.lsl readme")); + REQUIRE(index.prim(chair) != nullptr); CHECK(index.prim(chair)->name == std::string("Chair")); CHECK_FALSE(index.asking(chair)); @@ -330,7 +330,7 @@ namespace index.refresh(11.0); { INFO("its serial moved: asked"); - CHECK(pending.size() == 1); + REQUIRE(pending.size() == 1); CHECK(asked.back().first == chair); } answer(chair, { "sit.lsl", "new.lsl" }); diff --git a/indra/newview/tests/alscriptcrumbsbar_test.cpp b/indra/newview/tests/alscriptcrumbsbar_test.cpp index 9c71cd0680..8106cf3a29 100644 --- a/indra/newview/tests/alscriptcrumbsbar_test.cpp +++ b/indra/newview/tests/alscriptcrumbsbar_test.cpp @@ -41,6 +41,7 @@ #include +#include #include namespace @@ -158,6 +159,10 @@ namespace { SKIP("no UI: LLUI_TEST_APP_DIR does not point at the source tree"); } + // The jump bar the skin gives the bar, which every check after + // reads what the bar shows from. + INFO("the skin gives the bar its jump bar"); + REQUIRE(jump() != nullptr); return found; } ALJumpBar* jump() { return window.find("breadcrumb"); } @@ -229,11 +234,11 @@ namespace } return joined(out); } + // The part that begins so, or none. const Part* part(const std::string& begins) { - static std::vector held; - held = parts(); - for (const Part& one : held) + const std::vector& now = held.emplace_back(parts()); + for (const Part& one : now) { if (one.text.rfind(begins, 0) == 0) { @@ -242,6 +247,10 @@ namespace } return nullptr; } + // The parts each part() looked through, kept for the case: a part one + // call found is still there after the next, so a check that compares + // two reads both, whichever is found first. + std::list> held; }; TEST_CASE_METHOD(alscriptcrumbsbar_data, "alscriptcrumbsbar: the caret's path offers each step's siblings and follows changes", "[alscriptcrumbsbar]") @@ -253,7 +262,8 @@ namespace const std::string top = "Script a [default, f, g, h, other]"; CHECK(path() == top + ", default [default, f, g, h, other], state_entry [state_entry, touch_start]"); UNSCOPED_INFO("kept"); - REQUIRE(doc.caret->crumbPath.size() == size_t(2)); + CHECK(doc.caret->crumbPath.size() == size_t(2)); + REQUIRE(jump()->path().size() == size_t(3)); UNSCOPED_INFO("the script's step"); CHECK(jump()->path()[0].value == std::string("top")); UNSCOPED_INFO("said"); @@ -279,6 +289,7 @@ namespace CHECK(path().find("state_entry [") != std::string::npos); doc.editor->setCaret(ALTextPos(13, 5)); place(crumbs, doc); + REQUIRE_FALSE(jump()->path().empty()); UNSCOPED_INFO("where two meet: the later"); CHECK(jump()->path().back().label == std::string("h")); doc.editor->setCaret(ALTextPos(17, 0)); @@ -288,11 +299,13 @@ namespace doc.name = "Renamed"; place(crumbs, doc); + REQUIRE_FALSE(jump()->path().empty()); UNSCOPED_INFO("a new name: again"); CHECK(jump()->path()[0].label == std::string("Renamed")); Doc& other = tab("b"); other.name = "Other"; place(crumbs, other); + REQUIRE_FALSE(jump()->path().empty()); UNSCOPED_INFO("not in front: nothing"); CHECK(jump()->path()[0].label == std::string("Renamed")); crumbs->forget(); @@ -302,6 +315,7 @@ namespace CHECK(jump()->trailer().empty()); } place(crumbs, doc); + REQUIRE_FALSE(jump()->path().empty()); UNSCOPED_INFO("and shown again whatever"); CHECK(jump()->path()[0].label == std::string("Renamed")); } @@ -317,8 +331,10 @@ namespace CHECK(trailer() == std::string("Ln 3, Col 7, Tab Size: 4")); { INFO("pressed for a line"); - CHECK(part("Ln")->value == "line"); - CHECK(part("Ln")->toolTip == "line tip"); + const Part* line = part("Ln"); + REQUIRE(line != nullptr); + CHECK(line->value == "line"); + CHECK(line->toolTip == "line tip"); } doc.modifiable = false; @@ -344,20 +360,32 @@ namespace CHECK(raw[11].between); CHECK_FALSE(raw[12].between); } + const Part* indentation = part("Tab"); + const Part* selection = part("2 lines"); + const Part* errors = part("2 errors"); + const Part* warnings = part("1 warning"); + const Part* readOnly = part("Read"); + const Part* position = part("Ln"); + REQUIRE(indentation != nullptr); + REQUIRE(selection != nullptr); + REQUIRE(errors != nullptr); + REQUIRE(readOnly != nullptr); + REQUIRE(position != nullptr); { INFO("where narrow: the indentation first, then the selection, the counts and read only last, the place never"); - CHECK(part("Tab")->drop > part("2 lines")->drop); - CHECK(part("2 lines")->drop > part("2 errors")->drop); - CHECK(part("2 errors")->drop > 0); - CHECK(part("Read")->drop > 0); - CHECK(part("Ln")->drop == 0); + CHECK(indentation->drop > selection->drop); + CHECK(selection->drop > errors->drop); + CHECK(errors->drop > 0); + CHECK(readOnly->drop > 0); + CHECK(position->drop == 0); } UNSCOPED_INFO("read only, said"); - CHECK(part("Read")->toolTip == std::string("You can view this script but not change it")); + CHECK(readOnly->toolTip == std::string("You can view this script but not change it")); { INFO("the problems pressable"); - CHECK(part("2 errors")->value == "problems"); - CHECK(part("1 warning")->toolTip == "problems tip"); + CHECK(errors->value == "problems"); + REQUIRE(warnings != nullptr); + CHECK(warnings->toolTip == "problems tip"); } doc.editor->setSelection(ALTextRange(ALTextPos(2, 0), ALTextPos(4, 2))); crumbs->showTrailer(doc); @@ -370,9 +398,10 @@ namespace CHECK(part("4 characters selected") != nullptr); doc.editor->setCaret(ALTextPos(0, 2)); crumbs->showTrailer(doc); + const Part* column = part("Ln"); + REQUIRE(column != nullptr); UNSCOPED_INFO("the column as seen, past the tab"); - CHECK(part("Ln")->text == - "Ln 1, Col " + std::to_string(doc.editor->getTabWidth() + 1)); + CHECK(column->text == "Ln 1, Col " + std::to_string(doc.editor->getTabWidth() + 1)); doc.loaded = false; crumbs->showTrailer(doc); { @@ -407,14 +436,18 @@ namespace doc.weighing->sent = true; doc.weighing->weight->total = 60000; crumbs->showTrailer(doc); + weighed = part("LSL (Mono)"); + REQUIRE(weighed != nullptr); UNSCOPED_INFO("as sent"); - CHECK(part("LSL (Mono)")->toolTip.find("as it is saved") != std::string::npos); + CHECK(weighed->toolTip.find("as it is saved") != std::string::npos); UNSCOPED_INFO("near: the warning's"); - CHECK(part("LSL (Mono)")->color == warn); + CHECK(weighed->color == warn); doc.weighing->weight->total = 70000; crumbs->showTrailer(doc); + weighed = part("LSL (Mono)"); + REQUIRE(weighed != nullptr); UNSCOPED_INFO("past: the error's"); - CHECK(part("LSL (Mono)")->color == error); + CHECK(weighed->color == error); doc.weighing->weight->estimate = true; crumbs->showTrailer(doc); UNSCOPED_INFO("an estimate"); @@ -437,10 +470,12 @@ namespace } doc.weighing->assetBytes = LIMIT * 9 / 10 + 1; crumbs->showTrailer(doc); + REQUIRE_FALSE(parts().empty()); UNSCOPED_INFO("near: the warning's"); CHECK(parts().back().color == warn); doc.weighing->assetBytes = LIMIT + 1; crumbs->showTrailer(doc); + REQUIRE_FALSE(parts().empty()); UNSCOPED_INFO("over: the error's"); CHECK(parts().back().color == error); UNSCOPED_INFO("and by how much"); @@ -448,6 +483,7 @@ namespace doc.notecard = true; doc.weighing->assetBytes = static_cast(LLNotecard::MAX_SIZE) / 2 + 1; crumbs->showTrailer(doc); + REQUIRE_FALSE(parts().empty()); UNSCOPED_INFO("a notecard's by its own limit"); CHECK(parts().back().toolTip.find("Notecard text") != std::string::npos); doc.notecard = false; @@ -456,7 +492,8 @@ namespace crumbs->setTips({ "line tip", "problems tip", "source tip", "expanded tip" }); doc.expandedEditor = editor("expanded_a", "integer x;\n"); crumbs->showTrailer(doc); - const Part shown = parts().back(); + REQUIRE_FALSE(parts().empty()); + const Part shown = parts().back(); { INFO("the source in front"); CHECK(shown.text == "Source"); @@ -466,6 +503,7 @@ namespace doc.view = Doc::View::Expanded; doc.expandedEditor->setCaret(ALTextPos(0, 3)); crumbs->showTrailer(doc); + REQUIRE_FALSE(parts().empty()); { INFO("the expansion"); CHECK(parts().back().text == "Preprocessed"); @@ -493,18 +531,24 @@ namespace doc.uploaded.codeAfter = 10240; told().target = ALScriptWeight::Target::Mono; crumbs->showTrailer(doc); + const Part* weighed = part("LSL (Mono)"); + REQUIRE(weighed != nullptr); UNSCOPED_INFO("before and after"); - CHECK(part("LSL (Mono)")->text == std::string("LSL (Mono) ~20.0 \xE2\x86\x92 ~10.0 of 64 KB used")); + CHECK(weighed->text == std::string("LSL (Mono) ~20.0 \xE2\x86\x92 ~10.0 of 64 KB used")); UNSCOPED_INFO("once"); CHECK(parts().size() == size_t(4)); doc.uploaded.codeAfter = 60000; crumbs->showTrailer(doc); + weighed = part("LSL (Mono)"); + REQUIRE(weighed != nullptr); UNSCOPED_INFO("near: the warning's"); - CHECK(part("LSL (Mono)")->color == doc.editor->markColor(ALCodeEditor::Mark::Warning)); + CHECK(weighed->color == doc.editor->markColor(ALCodeEditor::Mark::Warning)); doc.uploaded.codeAfter = 70000; crumbs->showTrailer(doc); + weighed = part("LSL (Mono)"); + REQUIRE(weighed != nullptr); UNSCOPED_INFO("past: the error's"); - CHECK(part("LSL (Mono)")->color == doc.editor->markColor(ALCodeEditor::Mark::Error)); + CHECK(weighed->color == doc.editor->markColor(ALCodeEditor::Mark::Error)); told().target = ALScriptWeight::Target::LSO; crumbs->showTrailer(doc); { @@ -671,7 +715,9 @@ namespace { INFO("other tips: said again"); CHECK(said > 2); - CHECK(part("3 errors")->toolTip == "problems"); + const Part* problems = part("3 errors"); + REQUIRE(problems != nullptr); + CHECK(problems->toolTip == "problems"); } said = 0; Doc& other = tab("b"); diff --git a/indra/newview/tests/alscriptexplorermodel_test.cpp b/indra/newview/tests/alscriptexplorermodel_test.cpp index 207b7cced7..6254da059e 100644 --- a/indra/newview/tests/alscriptexplorermodel_test.cpp +++ b/indra/newview/tests/alscriptexplorermodel_test.cpp @@ -209,6 +209,18 @@ namespace const auto found = std::find_if(all.begin(), all.end(), [&name](const Model::Row& row) { return row.name == name; }); return found != all.end() ? &*found : nullptr; } + + // What the first row of that name stands for, as a click on it + // would choose it. + Model::Choice require_choice(const std::vector& all, const std::string& name) const + { + CAPTURE(name); + const Model::Row* row = rowNamed(all, name); + REQUIRE(row != nullptr); + const std::optional choice = Model::Choice::of(row->value); + REQUIRE(choice.has_value()); + return *choice; + } }; TEST_CASE_METHOD(alscriptexplorermodel_data, "alscriptexplorermodel: pins come first then the selection then the open scripts' objects", "[alscriptexplorermodel]") @@ -226,7 +238,7 @@ namespace selected = { chair, lamp }; open = { id(21) }; list(); - CHECK(names() == std::string("Lamp* Gone(away)* (unnamed)(away)* Chair Table")); + REQUIRE(names() == std::string("Lamp* Gone(away)* (unnamed)(away)* Chair Table")); { INFO("an object by a prim of it, listed by its root"); CHECK(model.objects().back().root == table); @@ -254,10 +266,14 @@ namespace const LLUUID chair = object(10, "Chair"); selected = { chair }; list(); + const std::vector unknown = model.rows(std::string()); + REQUIRE_FALSE(unknown.empty()); UNSCOPED_INFO("what a prim holds not known yet, a folder of it may still be opened"); - CHECK_FALSE(model.rows(std::string()).front().known); + CHECK_FALSE(unknown.front().known); holds(chair, { "sit.lsl", "readme" }); - CHECK(model.rows(std::string()).front().known); + const std::vector known = model.rows(std::string()); + REQUIRE_FALSE(known.empty()); + CHECK(known.front().known); CHECK(rows() == std::string("O:Chair I:sit.lsl I:readme")); list(); UNSCOPED_INFO("kept through a listing"); @@ -436,8 +452,8 @@ namespace holds(id(10), { "a.lsl", "b.lsl", "notes" }); holds(id(11), { "c.lsl" }); holds(id(12), {}); - const std::vector all = model.rows(std::string()); - Model::Choice whole = *Model::Choice::of(all.front().value); + const std::vector all = model.rows(std::string()); + const Model::Choice whole = require_choice(all, "House"); { INFO("an object row"); CHECK_FALSE(whole.isItem()); @@ -449,9 +465,9 @@ namespace UNSCOPED_INFO("each script once"); CHECK(model.scriptsReached({ whole }) == 3); - Model::Choice prim = *Model::Choice::of(rowNamed(all, "House.1")->value); - Model::Choice c = *Model::Choice::of(rowNamed(all, "c.lsl")->value); - Model::Choice a = *Model::Choice::of(rowNamed(all, "a.lsl")->value); + const Model::Choice prim = require_choice(all, "House.1"); + const Model::Choice c = require_choice(all, "c.lsl"); + const Model::Choice a = require_choice(all, "a.lsl"); { INFO("a prim row"); CHECK(prim.primRow); @@ -471,8 +487,9 @@ namespace } UNSCOPED_INFO("counted once"); CHECK(model.scriptsReached({ prim, c, a }) == 2); + const Model::Choice notes = require_choice(all, "notes"); UNSCOPED_INFO("a notecard chosen reaches no script"); - CHECK(model.scriptsReached({ *Model::Choice::of(rowNamed(all, "notes")->value) }) == 0); + CHECK(model.scriptsReached({ notes }) == 0); } TEST_CASE_METHOD(alscriptexplorermodel_data, "alscriptexplorermodel: a rename shows at once and a root's new name reaches its object's pin", "[alscriptexplorermodel]") @@ -482,6 +499,8 @@ namespace model.togglePinned(house, "House"); list(); model.renamed(id(11), "Door"); + REQUIRE(model.objects().size() == size_t(1)); + REQUIRE(model.objects().front().prims.size() == size_t(2)); CHECK(model.objects().front().prims[1].name == std::string("Door")); { INFO("a prim is not its object"); @@ -493,6 +512,7 @@ namespace INFO("the root is"); CHECK(model.objects().front().name == "Home"); CHECK(model.takePinsChanged()); + REQUIRE(model.pins().size() == size_t(1)); CHECK(model.pins().front().name == "Home"); } UNSCOPED_INFO("told once"); @@ -543,14 +563,14 @@ namespace // Pinned from the rows: every object among them, as the first is not. const std::vector all = model.rows(std::string()); - model.pin({ *Model::Choice::of(all[0].value), *Model::Choice::of(rowNamed(all, "b.lsl")->value), *Model::Choice::of(rowNamed(all, "Chair")->value) }); + model.pin({ require_choice(all, "House"), require_choice(all, "b.lsl"), require_choice(all, "Chair") }); { INFO("both pinned, each once"); CHECK(model.pins().size() == 2); CHECK(model.isPinned(house)); CHECK(model.isPinned(chair)); } - model.pin({ *Model::Choice::of(rowNamed(all, "Chair")->value) }); + model.pin({ require_choice(all, "Chair") }); { INFO("let go"); CHECK(model.pins().size() == 1); @@ -571,7 +591,7 @@ namespace CHECK(ask(false, false).empty()); model.askRegion(id(11)); UNSCOPED_INFO("the one dropped into, though its object says current"); - CHECK(ask(false, false) == std::vector({ id(11) })); + REQUIRE(ask(false, false) == std::vector({ id(11) })); { INFO("and of the region"); CHECK(model.asksRegion(id(11))); @@ -717,7 +737,10 @@ namespace { INFO("a step is no new place"); CHECK_FALSE(model.takePinsChanged()); - CHECK(model.pinOf(house)->position->mdV[VX] == 256010.0); + const Model::Pin* stepped = model.pinOf(house); + REQUIRE(stepped); + REQUIRE(stepped->position); + CHECK(stepped->position->mdV[VX] == 256010.0); } world[house].position = LLVector3d(256000.0 + 100.0, 256000.0 + 20.0, 30.0); world[house].region = "Morris"; @@ -725,7 +748,9 @@ namespace { INFO("far, and elsewhere: kept, and said"); CHECK(model.takePinsChanged()); - CHECK(model.pinOf(house)->region == "Morris"); + const Model::Pin* moved = model.pinOf(house); + REQUIRE(moved); + CHECK(moved->region == "Morris"); } // Across sessions, and away. diff --git a/indra/newview/tests/alscriptexplorertree_test.cpp b/indra/newview/tests/alscriptexplorertree_test.cpp index 48a91b4445..9ba2090f1e 100644 --- a/indra/newview/tests/alscriptexplorertree_test.cpp +++ b/indra/newview/tests/alscriptexplorertree_test.cpp @@ -394,8 +394,12 @@ namespace CHECK(drags == 1); } CHECK(t.choose(objectRow(house), false)); - UNSCOPED_INFO("an object may not"); - CHECK_FALSE(t.folderView()->getCurSelectedItem()->isMovable()); + item = t.folderView()->getCurSelectedItem(); + { + INFO("an object may not"); + REQUIRE(item); + CHECK_FALSE(item->isMovable()); + } gFocusMgr.setKeyboardFocus(nullptr); } } diff --git a/indra/newview/tests/alscriptexternaleditor_test.cpp b/indra/newview/tests/alscriptexternaleditor_test.cpp index 5803af8385..73ae638ae0 100644 --- a/indra/newview/tests/alscriptexternaleditor_test.cpp +++ b/indra/newview/tests/alscriptexternaleditor_test.cpp @@ -182,7 +182,7 @@ namespace CHECK(a.external->log->path() == file + ".log"); CHECK(studio.subscribed == Names{ "a" }); CHECK(a.external->subscribed); - CHECK(studio.started == Names{ file }); + REQUIRE(studio.started == Names{ file }); UNSCOPED_INFO("what it holds, known"); CHECK(a.external->written == std::string("default {}")); @@ -321,6 +321,7 @@ namespace ALScriptCompileResult result; result.messages = { "(1, 2) : ERROR : Syntax error\x07" }; unit.log(a, result); + REQUIRE(a.external->log); const std::string log = contents(a.external->log->path()); { INFO("the log: when, then the words, what cannot be printed dropped"); @@ -348,6 +349,7 @@ namespace ALScriptCompileResult result; unit.log(a, result); const std::string file = unit.fileName(a); + REQUIRE(a.external->log); const std::string log = a.external->log->path(); CHECK(exists(file)); CHECK(exists(log)); diff --git a/indra/newview/tests/alscriptinspectorpane_test.cpp b/indra/newview/tests/alscriptinspectorpane_test.cpp index 09adf68b36..e7c72b3ffa 100644 --- a/indra/newview/tests/alscriptinspectorpane_test.cpp +++ b/indra/newview/tests/alscriptinspectorpane_test.cpp @@ -268,7 +268,9 @@ namespace inspector->inspected(doc, result, ALTextPos(0, 8)); UNSCOPED_INFO("in the include"); CHECK(shown() == std::string("integer count\nDeclared in inc.lsl on line 5.")); - const ALTextView::Substitution& to = inspector->view()->substitutions().back(); + const std::vector& to_include = inspector->view()->substitutions(); + REQUIRE_FALSE(to_include.empty()); + const ALTextView::Substitution& to = to_include.back(); { INFO("linked there"); CHECK(to.value["path"].asString() == "disk:/inc.lsl"); @@ -327,7 +329,9 @@ namespace inspector->inspected(doc, result, ALTextPos(0, 8)); UNSCOPED_INFO("in the module"); CHECK(shown() == std::string("function M.twice(n: number): number\nDeclared in util.luau on line 4.")); - const ALTextView::Substitution& to = inspector->view()->substitutions().back(); + const std::vector& to_module = inspector->view()->substitutions(); + REQUIRE_FALSE(to_module.empty()); + const ALTextView::Substitution& to = to_module.back(); { INFO("linked there"); CHECK(to.value["path"].asString() == "disk:/lib/util.luau"); diff --git a/indra/newview/tests/alscriptkeymap_test.cpp b/indra/newview/tests/alscriptkeymap_test.cpp index 9979bdd2f1..009e895a73 100644 --- a/indra/newview/tests/alscriptkeymap_test.cpp +++ b/indra/newview/tests/alscriptkeymap_test.cpp @@ -332,8 +332,10 @@ namespace UNSCOPED_INFO("back on the Mac's Control-minus"); CHECK(has("back", ALKeyChord{ '-', MASK_MAC_CONTROL })); #else - UNSCOPED_INFO("back on Alt-Left and forward on Alt-Right"); - CHECK((has("back", ALKeyChord{ KEY_LEFT, MASK_ALT }) && has("forward", ALKeyChord{ KEY_RIGHT, MASK_ALT }))); + UNSCOPED_INFO("back on Alt-Left"); + CHECK(has("back", ALKeyChord{ KEY_LEFT, MASK_ALT })); + UNSCOPED_INFO("forward on Alt-Right"); + CHECK(has("forward", ALKeyChord{ KEY_RIGHT, MASK_ALT })); #endif } @@ -492,8 +494,8 @@ namespace UNSCOPED_INFO("and a problem's"); CHECK(has(ALScriptKeymap::menuKeys("next_problem"), ALKeyChord{ KEY_F8, MASK_NONE })); UNSCOPED_INFO("the editor's command of that name untouched"); - REQUIRE(ALScriptKeymap::keysOf(ALScriptKeymap::current(), C::NextChange).empty() == - ALScriptKeymap::keysOf(ALKeymap::standard(), C::NextChange).empty()); + CHECK(ALScriptKeymap::keysOf(ALScriptKeymap::current(), C::NextChange) == + ALScriptKeymap::keysOf(ALKeymap::standard(), C::NextChange)); ALScriptKeymap::restoreAll(); } } diff --git a/indra/newview/tests/alscriptlookup_test.cpp b/indra/newview/tests/alscriptlookup_test.cpp index 446e97c222..7becc51526 100644 --- a/indra/newview/tests/alscriptlookup_test.cpp +++ b/indra/newview/tests/alscriptlookup_test.cpp @@ -487,8 +487,11 @@ namespace REQUIRE(studio.loads.size() == size_t(1)); UNSCOPED_INFO("the open one passed over"); CHECK(studio.expands.empty()); - UNSCOPED_INFO("waiting said"); - CHECK(has(services.statuses.back(), "LookingAcross [COUNT]=1 [NAME]=count")); + { + INFO("waiting said"); + REQUIRE_FALSE(services.statuses.empty()); + CHECK(has(services.statuses.back(), "LookingAcross [COUNT]=1 [NAME]=count")); + } UNSCOPED_INFO("nothing shown yet"); CHECK(studio.shows == 0); @@ -559,6 +562,7 @@ namespace } studio.expands[0].expanded(expansion("B", ALScriptPreprocessor::pathOf(b), B_TEXT, "inc.lsl", inc, "integer count;\n")); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(answer({ span(0, 8, 5) })); { INFO("still one to come"); @@ -585,7 +589,11 @@ namespace studio.lines.clear(); unit->start(doc, ALEditorCommand::FindReferences, refsOf("count"), true, inc, span(0, 8, 5), {}, doc.editor->document().version()); + // This lookup's own questions answered, not the last one's. + CHECK(studio.expands.size() == size_t(2)); + CHECK(studio.loads.size() == size_t(2)); studio.expands.back().expanded(expansion("B", ALScriptPreprocessor::pathOf(b), B_TEXT, "inc.lsl", inc, "integer count;\n")); + CHECK(studio.asks.size() == size_t(2)); studio.asks.back().answered(answer({ span(0, 8, 5) })); studio.loads.back().loaded(LLUUID::null, "default { }\n"); UNSCOPED_INFO("shown again"); @@ -606,6 +614,7 @@ namespace lookUp(ALEditorCommand::FindReferences, false); UNSCOPED_INFO("the second shown"); CHECK(studio.shows == 1); + REQUIRE(studio.loads.size() == size_t(1)); studio.loads[0].loaded(LLUUID::null, B_TEXT); { INFO("the first's answer dropped"); @@ -614,7 +623,9 @@ namespace } lookUp(ALEditorCommand::FindReferences); + CHECK(studio.loads.size() == size_t(2)); studio.loads.back().loaded(LLUUID::null, B_TEXT); + REQUIRE(studio.expands.size() == size_t(1)); studio.expands.back().expanded(expansion("B", ALScriptPreprocessor::pathOf(b), B_TEXT)); UNSCOPED_INFO("no declaration in it: nothing asked"); CHECK(studio.asks.empty()); @@ -624,12 +635,15 @@ namespace const U32 generation = doc.lookup->generation; lookUp(ALEditorCommand::FindReferences); CHECK(doc.lookup->generation != generation); + CHECK(studio.loads.size() == size_t(3)); studio.loads.back().loaded(LLUUID::null, B_TEXT); + CHECK(studio.expands.size() == size_t(2)); // A lookup of another tab's, still waiting on its read, as this // one waits on its expansion: both current, and both gone with it. Doc& other = tab("x", c, A_TEXT, "X"); unit->start(other, ALEditorCommand::FindReferences, refsOf("count"), true, std::string(), span(0, 8, 5), {}, other.editor->document().version()); + CHECK(studio.loads.size() == size_t(4)); const size_t expands = studio.expands.size(); unit.reset(); studio.expands.back().expanded( @@ -650,6 +664,7 @@ namespace lookUp(ALEditorCommand::Rename, false, false); { INFO("not renamable"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(has(services.statuses.back(), "NotRenamable")); CHECK(studio.askedName.empty()); } @@ -702,6 +717,7 @@ namespace CHECK(has(studio.hint("sum"), "RenameToAcross [COUNT]=9 [FILES]=6")); studio.chosen("9lives"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(has(services.statuses.back(), "RenameBadName")); CHECK(doc.editor->text() == A_TEXT); studio.chosen("state"); @@ -743,8 +759,12 @@ namespace } UNSCOPED_INFO("a file opened"); CHECK(studio.filesOpened == Names{ "/e.lsl" }); - UNSCOPED_INFO("and changed"); - CHECK(services.findDoc("disk:/e.lsl")->editor->text() == std::string("integer total = 4;\n")); + const Doc* file = services.findDoc("disk:/e.lsl"); + { + INFO("and changed"); + REQUIRE(file); + CHECK(file->editor->text() == std::string("integer total = 4;\n")); + } UNSCOPED_INFO("said once"); REQUIRE(services.reports.size() == size_t(1)); const al_studio_test::FakeServices::Said& said = services.reports[0]; @@ -767,6 +787,7 @@ namespace Doc& doc = lookUp(ALEditorCommand::Rename, false); doc.editor->insertText(" "); studio.chosen("total"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(has(services.statuses.back(), "RenameStale [NAME]=total")); CHECK(services.reports.empty()); @@ -943,6 +964,7 @@ namespace studio.previewed("9lives"); { INFO("no name: nothing shown"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(has(services.statuses.back(), "RenameBadName")); CHECK_FALSE(studio.apply); } @@ -959,7 +981,9 @@ namespace CHECK_FALSE(has(studio.previewSaid, "RenamePreviewHint")); } studio.apply({ 1 }); - Doc& doc = *services.findDoc("a"); + Doc* found = services.findDoc("a"); + REQUIRE(found); + Doc& doc = *found; UNSCOPED_INFO("made at the one kept"); CHECK(doc.editor->text() == std::string("integer count;\ndefault { state_entry() { sum = 1; } }\n")); @@ -1017,6 +1041,7 @@ namespace CHECK(studio.shows == 0); studio.loads[1].loaded(LLUUID::null, std::string("default { }\n")); CHECK(studio.shows == 1); + REQUIRE_FALSE(services.statuses.empty()); const std::string said = services.statuses.back(); { INFO("found, then what was passed over: " + said); @@ -1101,6 +1126,7 @@ namespace UNSCOPED_INFO("beside it as it is"); CHECK(studio.compared.back() == "a: " + std::string(A_TEXT) + " | integer count;\ndefault { state_entry() { sum = 1; } }\n (CompareNow | RenameAfter [NAME]=sum [SCRIPT]=A)"); + REQUIRE_FALSE(studio.activated.empty()); UNSCOPED_INFO("in its tab, brought forward"); CHECK(studio.activated.back() == "a"); UNSCOPED_INFO("nothing changed"); @@ -1118,6 +1144,7 @@ namespace { INFO("not open: said, not shown"); CHECK(studio.compared.size() == before); + REQUIRE_FALSE(services.statuses.empty()); CHECK(has(services.statuses.back(), "RenameChangesNotHere [NAME]=sum [SCRIPT]=D")); CHECK(services.statusFailures.back()); } @@ -1251,6 +1278,7 @@ namespace } studio.expands[0].expanded(expansion("lib.luau", LIB, LIB_TEXT)); studio.expands[1].expanded(expansion("b.luau", B, B_LUA, "lib.luau", LIB, LIB_TEXT)); + REQUIRE(studio.asks.size() == size_t(2)); studio.asks[0].answered(answer({ span(1, 11, 5) })); studio.asks[1].answered(answer({ span(1, 11, 5), span(4, 10, 5) })); UNSCOPED_INFO("the other's place, by its path"); diff --git a/indra/newview/tests/alscriptnavigation_test.cpp b/indra/newview/tests/alscriptnavigation_test.cpp index b5688d568c..ab5e04378f 100644 --- a/indra/newview/tests/alscriptnavigation_test.cpp +++ b/indra/newview/tests/alscriptnavigation_test.cpp @@ -229,12 +229,17 @@ namespace a.editor->setCaret(ALTextPos(2, 0)); unit.noteJump(true); unit.goBack(false); + REQUIRE(studio.shown.size() == 3); UNSCOPED_INFO("a walk ended: the next is a jump of its own"); CHECK(studio.shown.back() == "a:2"); a.loaded = false; unit.noteJump(); - UNSCOPED_INFO("a tab not loaded: nothing noted"); - CHECK((!unit.canGo(false) || studio.shown.size() == 3)); + { + // Noting a place drops the way forward, a:2 here. + INFO("a tab not loaded: nothing noted"); + CHECK(unit.places(false) == 1); + CHECK(unit.canGo(true)); + } } TEST_CASE_METHOD(alscriptnavigation_data, "alscriptnavigation: a row stayed on opens as a preview unless already open or passed or left", "[alscriptnavigation]") diff --git a/indra/newview/tests/alscriptobjectcheck_test.cpp b/indra/newview/tests/alscriptobjectcheck_test.cpp index b8547080a5..f1382caa83 100644 --- a/indra/newview/tests/alscriptobjectcheck_test.cpp +++ b/indra/newview/tests/alscriptobjectcheck_test.cpp @@ -184,6 +184,7 @@ namespace { INFO("a check of what was read"); REQUIRE(window.asks[0].request.kind == ALScriptAnalysis::Kind::Check); + REQUIRE(window.asks[0].request.text); CHECK(*window.asks[0].request.text == "default {}\n"); } { @@ -269,6 +270,7 @@ namespace result.problems.push_back(said); window.expands[0].expanded(result); REQUIRE(window.asks.size() == size_t(1)); + REQUIRE(window.asks[0].request.text); UNSCOPED_INFO("about the expansion"); CHECK(*window.asks[0].request.text == std::string("integer x;\ndefault {}\n")); @@ -300,6 +302,7 @@ namespace { house(); check.check(root, "House"); + REQUIRE_FALSE(window.asks.empty()); std::function stale = std::move(window.asks[0].answered); window.asks.clear(); window.checked.clear(); diff --git a/indra/newview/tests/alscriptoutlinepane_test.cpp b/indra/newview/tests/alscriptoutlinepane_test.cpp index ea2467aa97..fa00471df5 100644 --- a/indra/newview/tests/alscriptoutlinepane_test.cpp +++ b/indra/newview/tests/alscriptoutlinepane_test.cpp @@ -174,10 +174,14 @@ namespace REQUIRE(rows() == std::string("count, ^default, .touch_start, .state_entry, alpha")); CHECK(joined(told().shown) == std::string("a")); const std::vector items = outline->list()->getAllData(); + const LLScrollListCell* kind = items[1]->getColumn(0); + const LLScrollListCell* name = items[0]->getColumn(1); + REQUIRE(kind != nullptr); + REQUIRE(name != nullptr); UNSCOPED_INFO("what it is"); - CHECK(items[1]->getColumn(0)->getToolTip() == std::string("state")); + CHECK(kind->getToolTip() == std::string("state")); UNSCOPED_INFO("and its declaration"); - CHECK(items[0]->getColumn(1)->getToolTip() == std::string("variable\ninteger")); + CHECK(name->getToolTip() == std::string("variable\ninteger")); UNSCOPED_INFO("by its index"); CHECK(items[4]->getValue().asInteger() == 4); CHECK(comment().empty()); @@ -238,6 +242,7 @@ namespace ALScriptOutlinePane* outline = pane(); Doc& doc = tab("a"); LLFilterEditor* filter = window.find("outline_filter"); + REQUIRE(filter != nullptr); filter->setText(std::string("ST")); filter->onCommit(); UNSCOPED_INFO("each with the letters"); @@ -254,6 +259,7 @@ namespace UNSCOPED_INFO("kept"); CHECK(outline->sortOrder() == std::string("name")); LLComboBox* sort = window.find("outline_sort"); + REQUIRE(sort != nullptr); sort->selectByValue(LLSD("kind")); sort->onCommit(); UNSCOPED_INFO("by kind, then name"); @@ -305,7 +311,14 @@ namespace Doc& doc = tab("a"); outline->show(doc); // A mark left on the first row, which a list made again does not have. - outline->list()->getFirstData()->getColumn(1)->setValue(LLSD("marked")); + const auto require_marked = [&]() { + LLScrollListItem* first = outline->list()->getFirstData(); + REQUIRE(first != nullptr); + LLScrollListCell* words = first->getColumn(1); + REQUIRE(words != nullptr); + words->setValue(LLSD("marked")); + }; + require_marked(); outline->show(doc); UNSCOPED_INFO("the same rows"); CHECK(cell(outline->list()->getFirstData(), 1) == std::string("marked")); @@ -313,7 +326,7 @@ namespace outline->show(doc); UNSCOPED_INFO("a detail changed: made again"); CHECK(cell(outline->list()->getFirstData(), 1) != "marked"); - outline->list()->getFirstData()->getColumn(1)->setValue(LLSD("marked")); + require_marked(); Doc& other = tab("b"); other.outline[0].detail = "float"; window.services().front = 1; @@ -339,12 +352,13 @@ namespace { INFO("gone to"); CHECK(joined(told().chosen) == "count"); + REQUIRE_FALSE(window.services().reveals.empty()); CHECK_FALSE(window.services().reveals.back()); } { INFO("return: gone to, to type there"); CHECK(list->handleKeyHere(KEY_RETURN, MASK_NONE)); - CHECK(told().chosen.back() == "count editor"); + CHECK(joined(told().chosen) == "count, count editor"); } UNSCOPED_INFO("the keyboard taken"); CHECK(window.services().reveals.back()); @@ -369,6 +383,7 @@ namespace window.floater->setVisible(true); const LLRect rect = list->getItemListRect(); const LLScrollListColumn* icon = list->getColumn("icon"); + REQUIRE(icon != nullptr); const auto row = [&](S32 at) { const LLScrollListItem* item = list->hitItem(rect.mLeft + 1, at); return item ? item->getValue().asInteger() : -1; @@ -438,6 +453,7 @@ namespace Doc& doc = tab("a"); outline->show(doc); const std::vector before = outline->list()->getAllData(); + REQUIRE(before.size() == 5); doc.caret->crumbPath = { 4 }; outline->followCaret(doc); UNSCOPED_INFO("alpha chosen"); diff --git a/indra/newview/tests/alscriptoutputpane_test.cpp b/indra/newview/tests/alscriptoutputpane_test.cpp index 28d97a5a96..2cc1598a90 100644 --- a/indra/newview/tests/alscriptoutputpane_test.cpp +++ b/indra/newview/tests/alscriptoutputpane_test.cpp @@ -162,16 +162,25 @@ namespace { LLComboBox* combo = window.find("output_filter"); CAPTURE(value); + REQUIRE(combo != nullptr); REQUIRE(combo->selectByValue(LLSD(value))); combo->onCommit(); } - void find(const std::string& words) + // Words typed in the find box, as a commit of it would take them. + void require_find(const std::string& words) { LLFilterEditor* editor = window.find("output_find"); + REQUIRE(editor != nullptr); editor->setText(words); editor->onCommit(); } - const ALOutputView::Entry& last() const { return pane->view()->entries().back(); } + // What the pane said last. + const ALOutputView::Entry& require_last() const + { + const auto& entries = pane->view()->entries(); + REQUIRE_FALSE(entries.empty()); + return entries.back(); + } }; TEST_CASE_METHOD(alscriptoutputpane_data, "ALScriptOutputPane: the kind filter picks errors or owner words or debug or the studio's", "[ALScriptOutputPane]") @@ -226,13 +235,13 @@ namespace std::string("|lamp speaks|crate tells its owner|Saved door.lsl.")); require_whose(""); - find("DOOR"); + require_find("DOOR"); UNSCOPED_INFO("the words, in the text or whose it is"); CHECK(shown() == std::string("|door speaks|Saved door.lsl.")); - find(" lamp "); + require_find(" lamp "); UNSCOPED_INFO("trimmed"); CHECK(shown() == std::string("|lamp speaks")); - find(""); + require_find(""); UNSCOPED_INFO("everything"); CHECK(shownTexts().size() == size_t(4)); @@ -272,8 +281,10 @@ namespace CHECK(out.objects().front().first == roots[6]); UNSCOPED_INFO("the next went"); CHECK(std::none_of(out.objects().begin(), out.objects().end(), [&](const auto& one) { return one.first == roots[7]; })); + const LLComboBox* filter = window.find("output_filter"); + REQUIRE(filter != nullptr); UNSCOPED_INFO("and is still chosen"); - CHECK(window.find("output_filter")->getValue().asString() == roots[6].asString()); + CHECK(filter->getValue().asString() == roots[6].asString()); } TEST_CASE_METHOD(alscriptoutputpane_data, "ALScriptOutputPane: an error or failure out of sight is unread until seen or cleared", "[ALScriptOutputPane]") @@ -307,7 +318,9 @@ namespace out.said("Could not save.", true, nullptr, {}); UNSCOPED_INFO("a failure out of sight is not"); CHECK(out.unread()); - window.find("output_clear")->onCommit(); + LLButton* clear = window.find("output_clear"); + REQUIRE(clear != nullptr); + clear->onCommit(); UNSCOPED_INFO("cleared"); CHECK_FALSE(out.unread()); UNSCOPED_INFO("with what was said"); @@ -319,7 +332,7 @@ namespace ALScriptOutputPane& out = require_pane(); ALScriptStudioDoc& doc = services.addDoc("door-script", ALScriptRef(fresh(), fresh()), "door.lsl"); out.said("Could not save door.lsl.", true, &doc, { "retry", "copy" }); - const ALOutputView::Entry& entry = last(); + const ALOutputView::Entry& entry = require_last(); UNSCOPED_INFO("the name and two things to do"); REQUIRE(entry.links.size() == size_t(3)); UNSCOPED_INFO("the name, where the words say it"); @@ -348,7 +361,8 @@ namespace // Said of no tab: nothing to link. out.said("Nothing open.", false, nullptr, { "retry" }); - CHECK(last().links.empty()); + const ALOutputView::Entry& unlinked = require_last(); + CHECK(unlinked.links.empty()); // The tab closed since: nothing asked of the window. services.docs.clear(); out.choose(chosen); @@ -387,7 +401,7 @@ namespace CHECK(at.line == 4); UNSCOPED_INFO("in the script"); CHECK(at.file.empty()); - const ALOutputView::Entry& entry = last(); + const ALOutputView::Entry& entry = require_last(); UNSCOPED_INFO("a link to it"); CHECK(entry.link); UNSCOPED_INFO("at that line"); @@ -428,7 +442,7 @@ namespace services.docs.clear(); UNSCOPED_INFO("as said"); CHECK(out.heard(error).line == 7); - chosen = last(); + chosen = require_last(); out.choose(chosen); { INFO("gone to as the region counts it"); @@ -586,7 +600,7 @@ namespace Event error = failed(object, item, "Counter", "bad\rvalue", -1); error.stack = { "lua_script:8: bad\rvalue", "lua_script:3 function helper" }; out.heard(error); - const ALOutputView::Entry& entry = last(); + const ALOutputView::Entry& entry = require_last(); UNSCOPED_INFO("its breaks as the log reads them"); CHECK(entry.text == std::string("bad\nvalue\nlua_script:3 function helper")); UNSCOPED_INFO("a link for the frame"); @@ -601,7 +615,7 @@ namespace // the line it is shown on, and what to do after it. ALScriptStudioDoc& doc = services.addDoc("door-script", ALScriptRef(fresh(), fresh()), "door.lsl"); out.said("Could not save\rdoor.lsl.", true, &doc, { "retry" }); - const ALOutputView::Entry& studio_said = last(); + const ALOutputView::Entry& studio_said = require_last(); UNSCOPED_INFO("the name and what to do"); REQUIRE(studio_said.links.size() == size_t(2)); { diff --git a/indra/newview/tests/alscriptproblemspane_test.cpp b/indra/newview/tests/alscriptproblemspane_test.cpp index 998c2d11f0..963289cff2 100644 --- a/indra/newview/tests/alscriptproblemspane_test.cpp +++ b/indra/newview/tests/alscriptproblemspane_test.cpp @@ -30,6 +30,7 @@ #include "../alscriptstudioweighing.h" #include "alpanefolds.h" +#include "alscriptfixes.h" #include "alpanelist.h" #include "llcheckboxctrl.h" #include "llcombobox.h" @@ -231,12 +232,16 @@ namespace void tick(const char* box, bool on) { LLCheckBoxCtrl* check = window.find(box); + CAPTURE(box); + REQUIRE(check); check->set(on); check->onCommit(); } void pick(const char* combo, const std::string& value) { LLComboBox* box = window.find(combo); + CAPTURE(combo); + REQUIRE(box); INFO("the combo offers " << value); REQUIRE(box->selectByValue(LLSD(value))); box->onCommit(); @@ -278,7 +283,10 @@ namespace { messages.push_back(row.message); } - REQUIRE(made.rows.size() == size_t(6)); + { + CAPTURE(messages); + REQUIRE(made.rows.size() == size_t(6)); + } UNSCOPED_INFO("the weight first, at no line"); CHECK(made.rows[0].origin == services.words("OriginWeight")); UNSCOPED_INFO("the weight a warning, the studio's reckoning"); @@ -354,9 +362,10 @@ namespace CHECK(out.held() == 3); UNSCOPED_INFO("said on the tab"); CHECK(studio.counts > 0); + LLCheckBoxCtrl* errors = window.find("problems_errors"); + REQUIRE(errors); UNSCOPED_INFO("counted on the box"); - CHECK(window.find("problems_errors")->getLabel() == - services.words("FilterErrorsCount", { { "[COUNT]", "1" } })); + CHECK(errors->getLabel() == services.words("FilterErrorsCount", { { "[COUNT]", "1" } })); tick("problems_errors", false); UNSCOPED_INFO("the filter kept"); @@ -369,14 +378,16 @@ namespace UNSCOPED_INFO("the lint's alone"); CHECK(shown() == "|door warning|door note|#" + services.counted("ProblemsHidden", 1)); pick("problems_origin", ""); - window.find("problems_filter")->setText(std::string("NOTE")); - window.find("problems_filter")->onCommit(); + LLFilterEditor* filter = window.find("problems_filter"); + REQUIRE(filter); + filter->setText(std::string("NOTE")); + filter->onCommit(); UNSCOPED_INFO("the words, in either case"); CHECK(shown() == "|door note|#" + services.counted("ProblemsHidden", 2)); UNSCOPED_INFO("rows to show: nothing said over them"); CHECK_FALSE(out.list()->saysEmpty()); - window.find("problems_filter")->setText(std::string()); - window.find("problems_filter")->onCommit(); + filter->setText(std::string()); + filter->onCommit(); pick("problems_scope", "all"); UNSCOPED_INFO("every script's, each under its name"); @@ -427,6 +438,7 @@ namespace gather(d); out.fill(&d); out.selectFirstError(false); + REQUIRE_FALSE(studio.chosen.empty()); UNSCOPED_INFO("the first error, the compiler's"); CHECK(studio.chosen.back().line == 1); UNSCOPED_INFO("taken to the script"); @@ -452,10 +464,12 @@ namespace CHECK(studio.chosen.back().line == 0); REQUIRE(studio.toEditor.back()); } - const size_t gone = studio.chosen.size(); + const size_t gone = studio.chosen.size(); + const size_t revealed = services.reveals.size(); { INFO("escape"); CHECK(out.list()->handleKeyHere(KEY_ESCAPE, MASK_NONE)); + REQUIRE(services.reveals.size() == revealed + 1); REQUIRE(services.reveals.back()); REQUIRE(studio.chosen.size() == gone); } @@ -534,6 +548,7 @@ namespace CHECK(studio.kinds[1] == std::string("door:")); out.act("copy_all"); + REQUIRE_FALSE(services.statuses.empty()); UNSCOPED_INFO("copied, and said"); CHECK(services.statuses.back() == services.counted("ProblemsCopied", 3)); @@ -604,6 +619,8 @@ namespace } UNSCOPED_INFO("the lint named"); CHECK(named); + UNSCOPED_INFO("by the name a NOLINT comment turns it off by"); + CHECK(ALScriptFixes::suppressed(unused, "integer x; // NOLINT(DeclaredButNotUsed)", "", false)); UNSCOPED_INFO("the error no number"); CHECK_FALSE(numbered); LLComboBox* origins = window.find("problems_origin"); @@ -704,7 +721,7 @@ namespace at = rows[i]->getValue()["owner"].asString().rfind("checked:", 0) == 0 ? static_cast(i) : at; } UNSCOPED_INFO("a row of it"); - CHECK(at >= 0); + REQUIRE(at >= 0); list()->selectNthItem(at); pane->choose(false); REQUIRE_FALSE(studio.chosen.empty()); @@ -792,6 +809,7 @@ namespace LLTabContainer* tabs = window.find("bottom_tabs"); Doc& door = doc("door"); door.check->analysis = { problem(S::Parser, V::Error, 1, "door error"), problem(S::Lint, V::Warning, 3, "door warning") }; + REQUIRE(tabs); tabs->selectTabByName("references_tab"); CHECK_FALSE(ALPaneFolds::inSight(&out)); const S32 said = studio.counts; @@ -1052,6 +1070,7 @@ namespace } choose("the compiler's, nowhere"); out.choose(false); + REQUIRE_FALSE(studio.chosen.empty()); UNSCOPED_INFO("chosen at none"); CHECK(studio.chosen.back().line == -1); choose("the compiler's, at 3"); diff --git a/indra/newview/tests/alscriptrecompile_test.cpp b/indra/newview/tests/alscriptrecompile_test.cpp index fe67d2fabf..47c5491713 100644 --- a/indra/newview/tests/alscriptrecompile_test.cpp +++ b/indra/newview/tests/alscriptrecompile_test.cpp @@ -156,6 +156,7 @@ namespace window.runs = { { id(11), false } }; run.recompile({ one(10, "A") }, { { prim, "House" } }, "mono"); REQUIRE(window.listedFor.size() == 1); + REQUIRE(window.listedFor[0].size() == 1); CHECK(window.listedFor[0].front().first == prim); CHECK(run.running()); REQUIRE(window.asked.size() == size_t(Recompile::AT_ONCE)); @@ -172,7 +173,7 @@ namespace UNSCOPED_INFO("A not known: the save asks"); CHECK_FALSE(window.asked[0].running.has_value()); - for (size_t i = 0; i < 4; ++i) + for (size_t i = 0; i < size_t(Recompile::AT_ONCE); ++i) { window.asked[i].told(compiled()); } @@ -224,6 +225,8 @@ namespace CHECK(said("CompileFailed", true)); UNSCOPED_INFO("one not sent said with why"); CHECK(said("SaveFailed", true)); + UNSCOPED_INFO("each but B said"); + REQUIRE(window.scripts.size() == 5); const Recompile::Script& c = window.scripts[1]; { INFO("C's rows, by the compiler"); diff --git a/indra/newview/tests/alscriptreferencespane_test.cpp b/indra/newview/tests/alscriptreferencespane_test.cpp index 46e7f84b33..ec222b1987 100644 --- a/indra/newview/tests/alscriptreferencespane_test.cpp +++ b/indra/newview/tests/alscriptreferencespane_test.cpp @@ -30,6 +30,7 @@ #include "alscriptstudio_fixture.h" #include "lltabcontainer.h" #include "llbutton.h" +#include "llclipboard.h" #include "llfocusmgr.h" #include "llscrolllistcell.h" #include "llscrolllistitem.h" @@ -156,6 +157,7 @@ namespace CHECK(window.pane().counts == 1); CHECK(lines() == joined(Names{ "A 1:9", "A 2:5", "B 1:9" })); const std::vector rows = refs->list()->getAllData(); + REQUIRE(rows.size() == size_t(3)); CHECK(cell(rows[0], 2) == std::string("declaration")); UNSCOPED_INFO("in the script it is declared in only"); CHECK(cell(rows[2], 2) == std::string()); @@ -166,12 +168,14 @@ namespace Found twice = found(); twice.places.push_back(place("", 0, 20, "integer count; count")); refs->show(twice); + REQUIRE(refs->list()->getItemCount() == 4); UNSCOPED_INFO("on the declaration's line, not it"); CHECK(cell(refs->list()->getAllData()[3], 2).empty()); Found in_b = found(); in_b.home = B; refs->show(in_b); + REQUIRE(refs->list()->getItemCount() == 3); UNSCOPED_INFO("declared in B: A's first line not it"); CHECK(cell(refs->list()->getAllData()[0], 2).empty()); refs->forget(); @@ -217,6 +221,7 @@ namespace refs->pump(); UNSCOPED_INFO("B's by its path"); CHECK(lines() == joined(Names{ "A 2:9", "A 3:5", "B 1:11" })); + REQUIRE(refs->found().places.size() == size_t(3)); UNSCOPED_INFO("its line's words"); CHECK(refs->found().places[2].text == std::string("foo() { count; }")); @@ -231,6 +236,7 @@ namespace refs->pump(); UNSCOPED_INFO("a place on the line edited"); CHECK(lines() == joined(Names{ "A 3:6", "B 1:11" })); + REQUIRE_FALSE(refs->found().places.empty()); UNSCOPED_INFO("its words again"); CHECK(refs->found().places[0].text == std::string("yy = count;")); const S32 counts = window.pane().counts; @@ -257,7 +263,7 @@ namespace refs->list()->onCommit(); refs->choose(true); UNSCOPED_INFO("gone to"); - CHECK(joined(window.pane().chosen) == joined(Names{ "a|" + B + "|0", "a|" + B + "|0 editor" })); + REQUIRE(joined(window.pane().chosen) == joined(Names{ "a|" + B + "|0", "a|" + B + "|0 editor" })); refs->rekey("elsewhere", "z"); UNSCOPED_INFO("another's id"); CHECK(refs->found().from == std::string("a")); @@ -273,10 +279,18 @@ namespace CHECK(window.pane().chosen.back() == "a2||0 editor"); const size_t gone = window.pane().chosen.size(); CHECK(refs->list()->handleKeyHere(KEY_ESCAPE, MASK_NONE)); + REQUIRE_FALSE(window.services().reveals.empty()); CHECK(window.services().reveals.back()); CHECK(window.pane().chosen.size() == gone); - UNSCOPED_INFO("copied"); CHECK(refs->list()->handleKeyHere('C', MASK_CONTROL)); + std::string copied; + LLClipboard::instance().pasteFromClipboard(copied); + { + INFO("the row chosen copied, and no other: " + copied); + CHECK(copied.find("integer count;") != std::string::npos); + CHECK(copied.find("x = count;") == std::string::npos); + CHECK(copied.find("foo() { count; }") == std::string::npos); + } } TEST_CASE_METHOD(alscriptreferencespane_data, "alscriptreferencespane: rows sort by a column and then by script and place", "[alscriptreferencespane]") @@ -363,6 +377,7 @@ namespace // Left out by Space on the row chosen: its box clear, its line as it is. refs->list()->selectNthItem(1); refs->list()->handleUnicodeCharHere(' '); + REQUIRE(refs->list()->getItemCount() == 3); UNSCOPED_INFO("left out"); CHECK_FALSE(refs->list()->getAllData()[1]->getColumn(2)->getValue().asBoolean()); UNSCOPED_INFO("its line as it is"); @@ -423,6 +438,7 @@ namespace CHECK_FALSE(changes->getVisible()); { INFO("each row as found"); + REQUIRE(refs->list()->getItemCount() == 3); CHECK(cell(refs->list()->getAllData()[0], 3) == "integer count;"); CHECK(refs->list()->getAllData()[0]->getColumn(2)->getValue().asString() != "1"); } @@ -442,6 +458,7 @@ namespace refs->pump(); UNSCOPED_INFO("slid, not listed again unseen"); CHECK(lines() == joined(Names{ "A 1:9", "A 2:5", "B 1:9" })); + REQUIRE_FALSE(refs->found().places.empty()); UNSCOPED_INFO("the places themselves slid"); CHECK(refs->found().places[0].span.line == 1); tabs->selectTabByName("references_tab"); @@ -459,8 +476,10 @@ namespace boost::signals2::scoped_connection in_a( a.editor->document().onChanged([refs, &a](const ALTextDocument::Edit& edit) { refs->slide(a, "object:1:a", edit); })); LLScrollListItem* in_b = refs->list()->rowWithKey("3"); - LLScrollListCell* b_line = in_b ? in_b->getColumn(1) : nullptr; + REQUIRE(in_b); + LLScrollListCell* b_line = in_b->getColumn(1); LLScrollListItem* second = refs->list()->rowWithKey("2"); + CHECK(second); refs->list()->selectByValue(LLSD(2)); a.editor->replaceAll({ { ALTextRange(ALTextPos(0, 0), ALTextPos(0, 0)), "// one\n" } }); refs->pump(); @@ -486,7 +505,7 @@ namespace } refs->show(many); UNSCOPED_INFO("a thousand and one row saying the rest"); - CHECK(refs->list()->getItemCount() == 1001); + REQUIRE(refs->list()->getItemCount() == 1001); CHECK(cell(refs->list()->getAllData().back(), 3) == window.services().counted("ReferencesUnlisted", 3)); UNSCOPED_INFO("all of them counted over the list"); CHECK(head().find("1003") != std::string::npos); diff --git a/indra/newview/tests/alscriptregionusage_test.cpp b/indra/newview/tests/alscriptregionusage_test.cpp index 5e1c3d1616..74cc9e1d73 100644 --- a/indra/newview/tests/alscriptregionusage_test.cpp +++ b/indra/newview/tests/alscriptregionusage_test.cpp @@ -169,6 +169,7 @@ namespace CHECK(heard == 1); REQUIRE(usage.usageOf(id(1))); REQUIRE(usage.usageOf(id(1))->urls == 2); + REQUIRE(usage.usageOf(id(2))); CHECK(usage.usageOf(id(2))->memory == 16384); LLSD parcel; parcel["parcels"][0]["objects"].append(object(id(3), 131072, 0)); @@ -212,20 +213,21 @@ namespace UNSCOPED_INFO("the attachments once a minute, whichever asks"); REQUIRE(attachmentAsks.size() == 1U); - // An answer after it is gone goes nowhere. + // An answer after its asker is gone goes nowhere, and passes by + // returning; one to an asker still here is heard. Told late = landAsks.back().second; - { - Usage gone(fake()); - } std::optional other; other.emplace(fake()); other->ask({ id(3) }, 300.0); + UNSCOPED_INFO("the other asks for itself"); + REQUIRE(landAsks.size() == 4U); Told orphaned = landAsks.back().second; other.reset(); + const S32 before = heard; orphaned(parcel); late(parcel); UNSCOPED_INFO("the one still here heard"); - REQUIRE(usage.usageOf(id(3)) != nullptr); + CHECK(heard == before + 1); } TEST_CASE_METHOD(alscriptregionusage_data, "ALScriptRegionUsage: an estate manager asks each owner's script time once a minute", "[ALScriptRegionUsage]") @@ -265,6 +267,7 @@ namespace // Memory first, then time: both kept. LLSD details; details["parcels"][0]["objects"].append(object(id(1), 65536, 0)); + REQUIRE_FALSE(landAsks.empty()); landAsks[0].second(details); Usage::times_t times; times[id(1)] = 0.125f; @@ -285,6 +288,7 @@ namespace } // Memory again: the time stays. landAsks[0].second(details); + REQUIRE(usage.usageOf(id(1))); UNSCOPED_INFO("time kept through memory"); CHECK(usage.usageOf(id(1))->time == 0.125f); @@ -296,9 +300,11 @@ namespace timeAsks[0].second(later); { INFO("left out: no time now, its memory kept"); + REQUIRE(usage.usageOf(id(1))); REQUIRE_FALSE(usage.usageOf(id(1))->hasTime()); CHECK(usage.usageOf(id(1))->hasMemory()); } + REQUIRE(usage.usageOf(id(2))); UNSCOPED_INFO("named: its time"); CHECK(usage.usageOf(id(2))->time == 0.5f); Usage::times_t full; @@ -307,6 +313,7 @@ namespace full[id(100 + n)] = 0.01f; } timeAsks[0].second(full); + REQUIRE(usage.usageOf(id(2))); UNSCOPED_INFO("a full answer leaving it out: its time stands"); CHECK(usage.usageOf(id(2))->time == 0.5f); } diff --git a/indra/newview/tests/alscriptsearchpane_test.cpp b/indra/newview/tests/alscriptsearchpane_test.cpp index c79d345d47..ee38d65c10 100644 --- a/indra/newview/tests/alscriptsearchpane_test.cpp +++ b/indra/newview/tests/alscriptsearchpane_test.cpp @@ -172,7 +172,9 @@ namespace UNSCOPED_INFO("the skin builds the tab as the pane"); REQUIRE(pane != nullptr); // In sight, its tab the one chosen, as when it is worked in. - window.find("bottom_tabs")->selectTabByName("search_tab"); + LLTabContainer* bottom = window.find("bottom_tabs"); + REQUIRE(bottom != nullptr); + bottom->selectTabByName("search_tab"); return *pane; } @@ -240,11 +242,13 @@ namespace } { INFO("escape"); - REQUIRE(out.list()->handleKeyHere(KEY_ESCAPE, MASK_NONE)); + CHECK(out.list()->handleKeyHere(KEY_ESCAPE, MASK_NONE)); + REQUIRE_FALSE(services.reveals.empty()); CHECK(services.reveals.back()); } UNSCOPED_INFO("copied"); CHECK(out.list()->handleKeyHere('C', MASK_CONTROL)); + REQUIRE(out.search().found()[0].lines.size() == size_t(2)); UNSCOPED_INFO("the line as listed, trimmed"); CHECK(out.search().found()[0].lines[1] == std::string("timer two")); CHECK(counted() == services.words("SearchCount", { { "[HITS]", services.counted("Matches", 2) }, @@ -304,6 +308,7 @@ namespace out.fetched(studio.fetches[1].generation, "Thing", away, "away.lsl", std::make_shared("timer\nother\n"), false); UNSCOPED_INFO("the second's kept"); REQUIRE(out.search().found().size() == size_t(1)); + REQUIRE_FALSE(out.search().found()[0].places.empty()); UNSCOPED_INFO("for what it looked for"); CHECK(out.search().found()[0].places[0].begin.line == 1); } @@ -331,16 +336,24 @@ namespace door.editor->insertText("yy"); out.typedIn(door); out.pump(); + // A script searched again keeps its place in found(), whether it + // holds places now or none: found() sized once, its places after + // each pump. + REQUIRE(out.search().found().size() == size_t(1)); + REQUIRE_FALSE(out.search().found()[0].places.empty()); UNSCOPED_INFO("not at once"); CHECK(out.search().found()[0].places[0].begin.column == 1); std::this_thread::sleep_for(std::chrono::milliseconds(700)); LLTabContainer* tabs = window.find("bottom_tabs"); + REQUIRE(tabs != nullptr); tabs->selectTabByName("problems_tab"); out.pump(); + REQUIRE_FALSE(out.search().found()[0].places.empty()); UNSCOPED_INFO("due, but out of sight: waiting"); CHECK(out.search().found()[0].places[0].begin.column == 1); tabs->selectTabByName("search_tab"); out.pump(); + REQUIRE_FALSE(out.search().found()[0].places.empty()); UNSCOPED_INFO("then, seen, where it is now"); CHECK(out.search().found()[0].places[0].begin.column == 3); out.replaceAll(); @@ -361,6 +374,7 @@ namespace studio.objects = { { root, "Thing", { door.ref, away, card } } }; studio.inHand = root; find("timer", "object"); + REQUIRE(studio.fetches.size() == size_t(2)); out.fetched(studio.fetches[0].generation, "Thing", away, "away.lsl", std::make_shared("timer\n"), false); out.fetched(studio.fetches[1].generation, "Thing", card, "card", std::make_shared("a timer\n"), true); UNSCOPED_INFO("three found"); @@ -401,6 +415,7 @@ namespace } UNSCOPED_INFO("and each is made"); CHECK(studio.applied.size() == size_t(2)); + REQUIRE_FALSE(services.reports.empty()); CHECK(services.reports.back().text == services.words("SearchReplaced", { { "[PLACES]", services.counted("Places", 3) }, { "[SCRIPTS]", services.counted("ScriptsNotecards", 3) } }) + "; " + @@ -560,8 +575,16 @@ namespace TEST_CASE_METHOD(alscriptsearchpane_data, "ALScriptSearchPane: Sources keeps a script by its text and drops the oldest past the limit", "[ALScriptSearchPane]") { + // The viewer's one store: emptied before the case and after it, + // however it ends. + struct Emptied + { + ALScriptSearch::Sources& sources; + ~Emptied() { sources.clear(); } + }; ALScriptSearch::Sources& sources = ALScriptSearch::Sources::instance(); sources.clear(); + const Emptied emptied{ sources }; const ALScriptRef one(LLUUID::generateNewID(), LLUUID::generateNewID()); const LLUUID asset = LLUUID::generateNewID(); sources.keep(one, LLUUID::null, { std::make_shared("timer"), "one.lsl", false }); @@ -610,7 +633,6 @@ namespace CHECK(sources.find(refs[0], assets[0])); CHECK(sources.find(refs[2], assets[2])); } - sources.clear(); } TEST_CASE_METHOD(alscriptsearchpane_data, "ALScriptSearchPane: a fetched script is sought apart and an earlier search's is dropped", "[ALScriptSearchPane]") @@ -664,6 +686,7 @@ namespace out.typedIn(door); std::this_thread::sleep_for(std::chrono::milliseconds(700)); out.pump(); + REQUIRE_FALSE(out.search().found()[0].places.empty()); UNSCOPED_INFO("typed in: searched again"); CHECK(out.search().found()[0].places[0].begin.column == 1); } diff --git a/indra/newview/tests/alscriptsnippets_test.cpp b/indra/newview/tests/alscriptsnippets_test.cpp index 5fde82db5a..99ace32c58 100644 --- a/indra/newview/tests/alscriptsnippets_test.cpp +++ b/indra/newview/tests/alscriptsnippets_test.cpp @@ -175,11 +175,11 @@ namespace CHECK_FALSE(LLFile::isfile(ALFileWrite::besideOf(file))); // A file that cannot be written is not touched: never opened for - // writing and left cut short. Root writes what it likes. + // writing and left cut short. #if !LL_WINDOWS if (geteuid() == 0) { - return; + SKIP("root writes what it likes"); } #endif const std::string written = read(file); @@ -278,8 +278,11 @@ namespace // A third body takes the next number free. merged = ALScriptSnippets::merge(own, { snippet("say", "llSay(0, \"c\");") }); - UNSCOPED_INFO("numbered past the taken"); - CHECK(own.back().name == std::string("say (3)")); + { + INFO("numbered past the taken"); + CHECK(merged.renamed == 1U); + CHECK(own.back().name == std::string("say (3)")); + } } TEST_CASE_METHOD(alscriptsnippets_data, "alscriptsnippets: a followed notecard's snippets go to their languages and are never saved", "[alscriptsnippets]") diff --git a/indra/newview/tests/alscriptstudioaccount_test.cpp b/indra/newview/tests/alscriptstudioaccount_test.cpp index ac0b930104..fe537260b7 100644 --- a/indra/newview/tests/alscriptstudioaccount_test.cpp +++ b/indra/newview/tests/alscriptstudioaccount_test.cpp @@ -37,6 +37,7 @@ namespace { LLSD state; state["rect"] = LLSD().with(0, 1).with(1, 2); + state["scroll_map"] = true; state["open"]["tabs"] = LLSD::emptyArray(); state["pinned"] = LLSD().with(0, "a"); state["recent_scripts"] = LLSD().with(0, "s"); @@ -52,8 +53,9 @@ namespace } { INFO("the shape, the switches and the files left"); - REQUIRE(state.has("rect")); - REQUIRE(state.has("recent_files")); + CHECK(state.has("rect")); + CHECK(state.has("scroll_map")); + CHECK(state.has("recent_files")); } { INFO("into the account's"); diff --git a/indra/newview/tests/alscriptstudiocaret_test.cpp b/indra/newview/tests/alscriptstudiocaret_test.cpp index 0c8de212b1..623bdfbd91 100644 --- a/indra/newview/tests/alscriptstudiocaret_test.cpp +++ b/indra/newview/tests/alscriptstudiocaret_test.cpp @@ -231,6 +231,7 @@ namespace INFO("none: said, no jump"); REQUIRE(studio.said.size() == 1); CHECK(navigation->unit.places(false) == 1); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back().find("NoDefinition [NAME]=count") == 0); } ask(doc, ALEditorCommand::GoToDefinition); @@ -263,6 +264,7 @@ namespace { INFO("finding it: not"); REQUIRE(studio.said.size() == 1); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back().find("NothingKnown") == 0); } caret.ask(doc, ALEditorCommand::GoToDefinition, ALTextRange(ALTextPos(1, 37), ALTextPos(1, 42))); @@ -352,10 +354,12 @@ namespace ask(doc, ALEditorCommand::GoToDefinition); result.references.definition = span(7, 0, 5); const size_t said = studio.said.size(); + const size_t told = services.statuses.size(); caret.answered(doc, result, ALTextPos(0, 8)); { INFO("declared where no source is: none"); REQUIRE(studio.said.size() == said); + REQUIRE(services.statuses.size() == told + 1); CHECK(services.statuses.back().find("NoDefinition") == 0); } } @@ -600,6 +604,7 @@ namespace { INFO("a module the expansion has not: none"); REQUIRE(studio.said.size() == 1); + REQUIRE(services.statuses.size() == size_t(1)); CHECK(services.statuses.back().find("NoDefinition") == 0); } @@ -610,6 +615,8 @@ namespace { INFO("an expansion of another text: none"); REQUIRE(studio.said.size() == 1); + // Said again, not the module's none still standing. + REQUIRE(services.statuses.size() == size_t(2)); CHECK(services.statuses.back().find("NoDefinition") == 0); } } diff --git a/indra/newview/tests/alscriptstudiochecking_test.cpp b/indra/newview/tests/alscriptstudiochecking_test.cpp index 69f0babf54..9bb2bac785 100644 --- a/indra/newview/tests/alscriptstudiochecking_test.cpp +++ b/indra/newview/tests/alscriptstudiochecking_test.cpp @@ -384,6 +384,7 @@ namespace CHECK(asked.kind == Kind::Check); CHECK(asked.id == "a"); CHECK(asked.version == version(doc)); + REQUIRE(asked.text); CHECK(*asked.text == doc.editor->text()); } UNSCOPED_INFO("with the window's settings"); @@ -438,6 +439,7 @@ namespace checking.ask(doc, Kind::Hover, ALTextPos(0, 1), ALTextPos(0, 1)); { INFO("a newer text its own"); + REQUIRE(studio.asks.back().request.text); REQUIRE(studio.asks.back().request.text != studio.asks[0].request.text); CHECK(*studio.asks.back().request.text == doc.editor->text()); } @@ -454,6 +456,7 @@ namespace entry.name = "count"; result.outline = { entry }; doc.editor->insertText("x"); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(result); { INFO("of another text: dropped"); @@ -530,6 +533,8 @@ namespace REQUIRE(studio.asks.size() == size_t(1)); { INFO("of the expansion, at its place"); + REQUIRE(studio.asks[0].request.text); + REQUIRE(doc.expanded.text); CHECK(*studio.asks[0].request.text == *doc.expanded.text); CHECK(studio.asks[0].request.line == 4); } @@ -544,6 +549,7 @@ namespace result.line = 4; result.column = 20; studio.asks[0].answered(result); + REQUIRE_FALSE(studio.told.empty()); UNSCOPED_INFO("at the source's place"); CHECK(studio.told.back() == std::string("inspect a 3:20")); result.line = 0; @@ -555,6 +561,7 @@ namespace checking.ask(doc, Kind::Check, ALTextPos(), ALTextPos()); doc.check->expansions += 1; doc.expanded.generation += 1; + REQUIRE(studio.asks.size() == size_t(3)); studio.asks[2].answered(answer(doc)); { INFO("an answer of an expansion since replaced: the check asked again"); @@ -633,6 +640,7 @@ namespace Doc& doc = tab("a"); preprocessing = true; checking.ask(doc, Kind::Check, ALTextPos(), ALTextPos()); + REQUIRE(expansions.size() == size_t(1)); expansions[0].second(expansion(SCRIPT)); ALScriptProblem own = problem(4, "own"); own.fixes = { fix("Put right", 4, 0, 3, "abc") }; @@ -653,6 +661,7 @@ namespace mine.span.endLine = 1; theirs.name = "helper"; result.outline = { mine, theirs }; + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(result); const ALScriptProblems& got = doc.check->analysis; UNSCOPED_INFO("the include's unused dropped"); @@ -664,6 +673,7 @@ namespace CHECK(got[0].line == 3); REQUIRE(got[0].file.empty()); REQUIRE(got[0].fixes.size() == 1); + REQUIRE_FALSE(got[0].fixes[0].edits.empty()); CHECK(got[0].fixes[0].edits[0].line == 3); } { @@ -701,6 +711,8 @@ namespace CHECK(doc.check->fragment->second); } checking.ask(doc, Kind::Check, ALTextPos(), ALTextPos()); + REQUIRE(studio.asks.size() == size_t(1)); + REQUIRE(studio.asks[0].request.text); UNSCOPED_INFO("a state put after it"); CHECK(*studio.asks[0].request.text == doc.editor->text() + "\ndefault{state_entry(){}}\n"); ALScriptProblem unused = problem(0, "helper unused", ALScriptProblem::Severity::Warning); @@ -752,14 +764,17 @@ namespace ALScriptAnalysis::Result result = answer(doc, { undeclared, other }); result.problems[0].line = 4; result.problems[1].line = 4; + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(result); + REQUIRE(doc.check->analysis.size() == size_t(2)); const ALScriptProblem& given = doc.check->analysis[0]; UNSCOPED_INFO("offered"); REQUIRE(given.fixes.size() == size_t(1)); { INFO("the include put in, preferred"); CHECK(given.fixes[0].preferred); - REQUIRE(given.fixes[0].edits[0].text.find("#include \"lib.lsl\"") != std::string::npos); + REQUIRE_FALSE(given.fixes[0].edits.empty()); + CHECK(given.fixes[0].edits[0].text.find("#include \"lib.lsl\"") != std::string::npos); } UNSCOPED_INFO("nothing gives the other"); REQUIRE(doc.check->analysis[1].fixes.empty()); @@ -776,6 +791,7 @@ namespace studio.asks.back().answered(answer(doc, { undeclared })); { INFO("not preprocessed: nothing to include with"); + REQUIRE(doc.check->analysis.size() == size_t(1)); REQUIRE(doc.check->analysis[0].fixes.empty()); REQUIRE(nearby.size() == 1); } @@ -786,6 +802,7 @@ namespace studio.asks.back().answered(given_all); { INFO("all given: nothing fetched"); + REQUIRE(doc.check->analysis.size() == size_t(1)); REQUIRE_FALSE(doc.check->analysis[0].fixes.empty()); REQUIRE(nearby.size() == 1); } @@ -818,6 +835,7 @@ namespace ALScriptProblem theirs = parse; theirs.file = "disk:/lib.lsl"; checking.ask(doc, Kind::Check, ALTextPos(), ALTextPos()); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(answer(doc, { quiet, said, parse, theirs })); UNSCOPED_INFO("the one a comment quiets dropped"); REQUIRE(doc.check->analysis.size() == size_t(3)); @@ -840,6 +858,7 @@ namespace plain.map.finish(); expansions.back().second(plain); studio.asks.back().answered(answer(doc, { parse })); + REQUIRE(doc.check->analysis.size() == size_t(1)); UNSCOPED_INFO("its transform on: not"); CHECK(doc.check->analysis[0].message == std::string("syntax error")); @@ -866,6 +885,7 @@ namespace Doc& marked = tab("m", "integer f() inline { return 1; }\n"); checking.ask(marked, Kind::Check, ALTextPos(), ALTextPos()); studio.asks.back().answered(answer(marked, { problem(0, "syntax error") })); + REQUIRE(marked.check->analysis.size() == size_t(1)); UNSCOPED_INFO("inline explained: " << marked.check->analysis[0].message); CHECK(marked.check->analysis[0].message.find("syntax error PreprocHintInline [WORD]=inline") == 0); preprocessing = true; @@ -877,6 +897,7 @@ namespace stripped.map.finish(); expansions.back().second(stripped); studio.asks.back().answered(answer(marked, { problem(0, "syntax error") })); + REQUIRE(marked.check->analysis.size() == size_t(1)); UNSCOPED_INFO("preprocessing, the extensions off: not"); CHECK(marked.check->analysis[0].message == std::string("syntax error")); @@ -885,6 +906,7 @@ namespace Doc& constant = tab("c", "f(const integer n) { }\n"); checking.ask(constant, Kind::Check, ALTextPos(), ALTextPos()); studio.asks.back().answered(answer(constant, { problem(0, "syntax error") })); + REQUIRE(constant.check->analysis.size() == size_t(1)); UNSCOPED_INFO("const explained: " << constant.check->analysis[0].message); CHECK(constant.check->analysis[0].message.find("syntax error PreprocHintConst [WORD]=const") == 0); @@ -942,6 +964,7 @@ namespace CHECK(doc.editor->undoJournal().undoLabel() == std::string("fix")); { INFO("said, checked again"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back() == "Rename it"); CHECK(doc.check->analysisDue == 1.0); } @@ -976,11 +999,13 @@ namespace { INFO("not checked: checked first"); REQUIRE(doc.check->fixAllAfterCheck.has_value()); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back() == "FixChecking [NAME]=a"); } checking.pump(1.0); ALScriptProblem one = problem(0, "one"); one.fixes = { fix("First", 0, 0, 7, "float") }; + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(answer(doc, { one })); { INFO("then made"); @@ -1031,6 +1056,7 @@ namespace ALScriptProblem one = problem(0, "one"); one.fixes = { fix("First", 0, 0, 7, "float") }; checking.ask(doc, Kind::Check, ALTextPos(), ALTextPos()); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(answer(doc, { one, problem(2, "two") })); { INFO("asked for, made with the next frame"); @@ -1044,6 +1070,7 @@ namespace REQUIRE(studio.waiting.empty()); REQUIRE(fixes.size() == 1); CHECK(fixes[0].title == "First"); + REQUIRE_FALSE(fixes[0].edits.empty()); CHECK(fixes[0].edits[0].second == "float"); } const Doc::Shown* shown = checking.shownOf(fixes[0].value); @@ -1104,6 +1131,7 @@ namespace ALScriptStudioChecking& checking = make(); Doc& doc = tab("a"); checking.ask(doc, Kind::Actions, ALTextPos(0, 8), ALTextPos(0, 13)); + REQUIRE(studio.asks.size() == size_t(1)); UNSCOPED_INFO("a stretch"); CHECK(studio.asks[0].request.endColumn == 13); { @@ -1141,6 +1169,7 @@ namespace refs.line = 0; refs.column = 8; studio.asks.back().answered(refs); + REQUIRE_FALSE(studio.told.empty()); UNSCOPED_INFO("handed on"); CHECK(studio.told.back() == std::string("references a 0:8")); doc.language.compileTarget = "none"; @@ -1233,6 +1262,7 @@ namespace Doc& doc = tab("a"); checking.schedule(doc, true); checking.pump(1.0); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks[0].answered(answer(doc, { problem(0, "wrong"), problem(1, "unused", ALScriptProblem::Severity::Warning) })); UNSCOPED_INFO("both taken"); REQUIRE(doc.check->analysis.size() == size_t(2)); @@ -1317,6 +1347,7 @@ namespace UNSCOPED_INFO("asked"); REQUIRE(studio.asks.size() == size_t(1)); const ALScriptAnalysis::Request& asked = studio.asks[0].request; + REQUIRE(asked.text); UNSCOPED_INFO("of the script apart"); CHECK(*asked.text == doc.editor->text()); { @@ -1362,7 +1393,11 @@ namespace // `count` declared const: in the expansion, a line down. ALPreprocessor::Result expanded = expansion(SCRIPT); expanded.consts = { { "count", 1, 8, false } }; + REQUIRE(expansions.size() == size_t(1)); expansions[0].second(expanded); + // The check, asked once the expansion came: what each inspect + // below is asked after. + REQUIRE(studio.asks.size() == size_t(1)); const auto inspect = [&](S32 line, S32 column, const std::string& label) { checking.ask(doc, Kind::Inspect, ALTextPos(0, 8), ALTextPos(0, 8)); ALScriptAnalysis::Result result = answer(doc); @@ -1432,8 +1467,10 @@ namespace checking.ask(doc, Kind::Inspect, ALTextPos(0, 10), ALTextPos(0, 10)); UNSCOPED_INFO("the inspector, of the same word: not asked"); REQUIRE(studio.asks.size() == size_t(1)); + REQUIRE_FALSE(studio.told.empty()); UNSCOPED_INFO("but told"); CHECK(studio.told.back() == std::string("inspect a 0:10")); + REQUIRE_FALSE(studio.labels.empty()); UNSCOPED_INFO("what the tip was"); CHECK(studio.labels.back() == std::string("integer count")); checking.ask(doc, Kind::Inspect, ALTextPos(1, 0), ALTextPos(1, 0)); @@ -1511,12 +1548,14 @@ namespace ALScriptProblem two = problem(3, "two"); two.fixes = { fix("Second", 3, 4, 15, "touch_start") }; const std::string before = doc.editor->wholeText(); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks.back().answered(answer(doc, { one, two })); UNSCOPED_INFO("asked, with a preview"); REQUIRE((bool)studio.preview); studio.preview(); UNSCOPED_INFO("nothing made yet"); CHECK(doc.editor->wholeText() == before); + REQUIRE_FALSE(studio.told.empty()); const std::string& compared = studio.told.back(); { INFO("the text now beside it after: " << compared); @@ -1526,6 +1565,7 @@ namespace } { INFO("Apply offered"); + REQUIRE_FALSE(services.reports.empty()); CHECK(services.reports.back().actions == Names{ "apply_fixes" }); CHECK(services.reports.back().doc == "a"); } @@ -1557,6 +1597,7 @@ namespace } { INFO("said"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back() == "FixAllChangedSince"); CHECK(services.statusFailures.back()); } @@ -1578,6 +1619,7 @@ namespace compat.source = ALScriptProblem::Source::Lint; compat.code = "SlCompatCall"; compat.fixes = { fix("Write it ll.GetSubString(s, 1, 3)", 2, 6, 36, "ll.GetSubString(s, 1, 3)") }; + REQUIRE(studio.asks.size() == size_t(1)); studio.asks.back().answered(answer(doc, { compat })); const ALScriptProblems& said = doc.check->analysis; UNSCOPED_INFO("two notes, then the lint"); @@ -1640,6 +1682,7 @@ namespace const ALScriptProblem zero = lint(0, "SlZeroIndex", fix("First", 0, 0, 7, "float", false)); const ALScriptProblem paren = lint(3, "SlParenCondition", fix("Second", 3, 4, 15, "touch_start", true)); const ALScriptProblem shadow = lint(1, "LocalShadow", fix("Third", 1, 0, 1, "x", true)); + REQUIRE(studio.asks.size() == size_t(1)); studio.asks.back().answered(answer(doc, { zero, paren, shadow })); Doc::FixPick pick; pick.migration = true; @@ -1647,12 +1690,14 @@ namespace checking.askFixAll(doc, pick); UNSCOPED_INFO("not asked: seen"); REQUIRE_FALSE(studio.preview); + REQUIRE_FALSE(studio.told.empty()); const std::string& compared = studio.told.back(); { INFO("the two from LSL, not the other: " << compared); REQUIRE(compared.find("FixAllAfter [FIXES]=Fixes [COUNT]=2") != std::string::npos); REQUIRE(compared.find("float count;") != std::string::npos); } + REQUIRE_FALSE(services.reports.empty()); UNSCOPED_INFO("Apply offered"); CHECK(services.reports.back().actions == Names{ "apply_fixes" }); { @@ -1685,6 +1730,7 @@ namespace truth.source = ALScriptProblem::Source::Lint; truth.code = "SlNumberTruth"; truth.fixes = { fix("Compare it: #t ~= 0", 1, 3, 5, "#t ~= 0", false) }; + REQUIRE(studio.asks.size() == size_t(1)); studio.asks.back().answered(answer(doc, { truth })); Doc::FixPick pick; pick.migration = true; @@ -1692,8 +1738,10 @@ namespace { INFO("previewed, not asked"); REQUIRE_FALSE(studio.preview); + REQUIRE_FALSE(studio.told.empty()); CHECK(studio.told.back().rfind("compare a: ", 0) == 0); } + REQUIRE_FALSE(services.reports.empty()); UNSCOPED_INFO("Apply offered"); CHECK(services.reports.back().actions == Names{ "apply_fixes" }); UNSCOPED_INFO("made by Apply"); @@ -1703,6 +1751,7 @@ namespace { INFO("Apply again: nothing waiting, and said"); CHECK_FALSE(checking.applyPreviewed(doc)); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back() == "FixAllNotPreviewed"); CHECK(services.statusFailures.back()); } @@ -1755,6 +1804,7 @@ namespace CHECK(named == std::vector{ "inc=/lib" }); { INFO("said"); + REQUIRE_FALSE(services.statuses.empty()); CHECK(services.statuses.back().find("AliasTaken [NAME]=inc") == 0); CHECK(services.statusFailures.back()); } @@ -1799,6 +1849,7 @@ namespace highlighter.tokens(last); UNSCOPED_INFO("the text not lexed to its end for it: " << highlighter.lastLexed() << " of " << last + 1 << " left"); CHECK(highlighter.lastLexed() > last / 2); + REQUIRE(doc.check->fragment); UNSCOPED_INFO("nor kept for the text as it stands"); CHECK(doc.check->fragment->first != version(doc)); { @@ -1814,6 +1865,7 @@ namespace checking.ask(doc, Kind::Check, ALTextPos(), ALTextPos()); UNSCOPED_INFO("asked once"); REQUIRE(studio.asks.size() == size_t(1)); + REQUIRE(studio.asks[0].request.text); UNSCOPED_INFO("of the script as it stands"); CHECK(*studio.asks[0].request.text == doc.editor->text()); UNSCOPED_INFO("and a script now"); diff --git a/indra/newview/tests/alscriptstudiocommands_test.cpp b/indra/newview/tests/alscriptstudiocommands_test.cpp index 6ce86db936..31f965fbed 100644 --- a/indra/newview/tests/alscriptstudiocommands_test.cpp +++ b/indra/newview/tests/alscriptstudiocommands_test.cpp @@ -87,7 +87,7 @@ namespace CHECK_FALSE(commands.addUnlisted("save", [this]() { done.push_back("third"); })); commands.run("save"); UNSCOPED_INFO("the first's"); - CHECK(done == std::vector{ "first" }); + REQUIRE(done == std::vector{ "first" }); UNSCOPED_INFO("an unlisted one is a command like any"); CHECK(commands.addUnlisted("reveal", [this]() { done.push_back("reveal"); })); commands.run("reveal"); @@ -142,7 +142,7 @@ namespace }); commands.run("first"); UNSCOPED_INFO("ran to its end"); - CHECK(done == std::vector{ "first" }); + REQUIRE(done == std::vector{ "first" }); commands.run("made_63"); UNSCOPED_INFO("and what it made runs"); CHECK(done.back() == std::string("made")); diff --git a/indra/newview/tests/alscriptstudiocomparepairs_test.cpp b/indra/newview/tests/alscriptstudiocomparepairs_test.cpp index 1c47b0a435..0336717edd 100644 --- a/indra/newview/tests/alscriptstudiocomparepairs_test.cpp +++ b/indra/newview/tests/alscriptstudiocomparepairs_test.cpp @@ -59,6 +59,7 @@ namespace ALScriptAnalysis::Result result; result.kind = one.request.kind; result.id = one.request.id; + REQUIRE(one.request.text); result.outline = ALLuauService::shape(*one.request.text); one.answered(result); } @@ -134,6 +135,7 @@ namespace CHECK(left.kind == ALScriptAnalysis::Kind::Shape); CHECK(left.lua); CHECK(left.front); + REQUIRE(left.text); CHECK(*left.text == LEFT); CHECK(left.id != studio.asked[1].request.id); } diff --git a/indra/newview/tests/alscriptstudiocomparewith_test.cpp b/indra/newview/tests/alscriptstudiocomparewith_test.cpp index fe46751c14..ce6b585ab4 100644 --- a/indra/newview/tests/alscriptstudiocomparewith_test.cpp +++ b/indra/newview/tests/alscriptstudiocomparewith_test.cpp @@ -228,6 +228,7 @@ namespace Doc& doc = tab("door", "as saved\n"); require_changed(doc, "as changed\n"); unit->show(doc); + REQUIRE_FALSE(studio.candidates.empty()); CHECK(studio.values().front() == std::string("saved")); CHECK(studio.candidates[0].label == said("CompareSaved")); studio.choose("saved"); @@ -238,6 +239,7 @@ namespace studio.choose("saved"); UNSCOPED_INFO("not compared"); CHECK(studio.did.size() == 1U); + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("CompareNothingSaved")); UNSCOPED_INFO("as a failure"); CHECK(services().statusFailures.back()); @@ -264,6 +266,7 @@ namespace studio.choose("saved_there"); UNSCOPED_INFO("nothing more"); CHECK(studio.did.size() == 1U); + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("CompareSavedThereGone")); UNSCOPED_INFO("as a failure"); CHECK(services().statusFailures.back()); @@ -294,6 +297,7 @@ namespace unsaved["[TITLE]"] = "lamp"; // Titled as a copy of it as it was, which stays as it is. const auto snapshot = [this](const std::string& name) { return this->said("CompareTabSnapshot", { { "[NAME]", name } }); }; + REQUIRE_FALSE(studio.did.empty()); UNSCOPED_INFO("unsaved, as it is now"); CHECK(studio.did.back() == "compare door: lamp later\n | door\n (" + snapshot(said("CompareUnsaved", unsaved)) + " | )"); @@ -308,6 +312,7 @@ namespace studio.choose("tab:sign"); UNSCOPED_INFO("nothing more"); CHECK(studio.did.size() == 2U); + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("CompareTabClosed", { { "[NAME]", "sign" } })); UNSCOPED_INFO("as a failure"); CHECK(services().statusFailures.back()); @@ -345,6 +350,7 @@ namespace studio.choose("clipboard"); UNSCOPED_INFO("not once emptied"); CHECK(studio.did.size() == 1U); + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("CompareWithClipboardEmpty")); } @@ -378,6 +384,7 @@ namespace CHECK(studio.did.size() == 1U); LLStringUtil::format_map_t args; args["[FILE]"] = missing; + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("LoadFromFileFailed", args)); UNSCOPED_INFO("as a failure"); CHECK(services().statusFailures.back()); diff --git a/indra/newview/tests/alscriptstudiodoc_test.cpp b/indra/newview/tests/alscriptstudiodoc_test.cpp index 5fe96f5b05..d939000fad 100644 --- a/indra/newview/tests/alscriptstudiodoc_test.cpp +++ b/indra/newview/tests/alscriptstudiodoc_test.cpp @@ -204,8 +204,12 @@ namespace } UNSCOPED_INFO("the whole line"); CHECK(util->range == ALTextRange(ALTextPos(1, 0), ALTextPos(1, lsl.lineLength(1)))); - UNSCOPED_INFO("angled"); - CHECK(Doc::namedIn(lsl, ALTextPos(2, 12), false, none)->name == "sys.lsl"); + const std::optional sys = Doc::namedIn(lsl, ALTextPos(2, 12), false, none); + { + INFO("angled"); + REQUIRE(sys.has_value()); + CHECK(sys->name == "sys.lsl"); + } { INFO("not a string elsewhere"); CHECK_FALSE(Doc::namedIn(lsl, ALTextPos(3, 12), false, none)); diff --git a/indra/newview/tests/alscriptstudioexpandedcompare_test.cpp b/indra/newview/tests/alscriptstudioexpandedcompare_test.cpp index 5c42c6bd93..1bbd84c7ab 100644 --- a/indra/newview/tests/alscriptstudioexpandedcompare_test.cpp +++ b/indra/newview/tests/alscriptstudioexpandedcompare_test.cpp @@ -185,6 +185,7 @@ namespace CHECK(studio.titles == "door | " + services().words("ComparePreprocessed")); UNSCOPED_INFO("lined up by line"); CHECK(said(studio.given) == std::string("[0-0 1-1][1-1 2-2]")); + REQUIRE(door.compareView); ALDiffView& view = *door.compareView; UNSCOPED_INFO("the source's first line beside the expansion's second"); CHECK(view.left()->layout().lineTop(0) == view.right()->layout().lineTop(1)); @@ -293,6 +294,7 @@ namespace door.uploaded.text = std::make_shared(result.text); door.uploaded.map = result.map; unit->compare(door); + REQUIRE(door.compareView); ALDiffView& view = *door.compareView; const std::vector out = ALTextDiff::split(result.text); const auto where = [&out](std::string_view what) { @@ -340,6 +342,7 @@ namespace door.editor->insertText("// "); unit->compare(door); CHECK(studio.runs == 1); + REQUIRE_FALSE(services().statuses.empty()); UNSCOPED_INFO("said while it waits"); CHECK(services().statuses.back() == services().words("Preprocessing", { { "[NAME]", "door" } })); // The window was busy: the run that answers is the one before. diff --git a/indra/newview/tests/alscriptstudiofiles_test.cpp b/indra/newview/tests/alscriptstudiofiles_test.cpp index d8b9eafdc0..dc50501e0c 100644 --- a/indra/newview/tests/alscriptstudiofiles_test.cpp +++ b/indra/newview/tests/alscriptstudiofiles_test.cpp @@ -330,7 +330,9 @@ namespace REQUIRE_FALSE(services.opened.empty()); CHECK(services.opened.back().ref == a.ref); } - studio.menu->findChild("clear_recent")->onCommit(); + LLMenuItemCallGL* clear = studio.menu->findChild("clear_recent"); + REQUIRE(clear != nullptr); + clear->onCommit(); { INFO("the clear item clears"); CHECK(unit.recentFiles().empty()); @@ -350,6 +352,7 @@ namespace CHECK(contents(f.file) == std::string("new")); { INFO("its own write is no change to the watch"); + REQUIRE(f.watch); CHECK_FALSE(f.watch->check()); CHECK_FALSE(f.watch->check()); } @@ -472,8 +475,8 @@ namespace TEST_CASE_METHOD(alscriptstudiofiles_data, "alscriptstudiofiles: Save to File writes a copy and Save As makes the tab the new file", "[alscriptstudiofiles]") { ALScriptStudioFiles& unit = make(); - Doc& s = scriptTab("s", "script text"); - services.front = 0; + scriptTab("s", "script text"); + services.front = 0; unit.saveCopy(); UNSCOPED_INFO("named after the tab"); CHECK(studio.savedAs == std::string("s")); @@ -503,8 +506,11 @@ namespace } { INFO("noted, settled, checked"); + REQUIRE_FALSE(unit.recentFiles().empty()); CHECK(unit.recentFiles().front() == in("g.luau")); + REQUIRE_FALSE(studio.settled.empty()); CHECK(studio.settled.back() == f.id); + REQUIRE_FALSE(studio.checked.empty()); CHECK(studio.checked.back() == f.id + " now"); } UNSCOPED_INFO("told of as written"); diff --git a/indra/newview/tests/alscriptstudiohistory_test.cpp b/indra/newview/tests/alscriptstudiohistory_test.cpp index 43586af280..358cb9d0b8 100644 --- a/indra/newview/tests/alscriptstudiohistory_test.cpp +++ b/indra/newview/tests/alscriptstudiohistory_test.cpp @@ -193,6 +193,7 @@ namespace REQUIRE(studio.candidates.size() == 3U); CHECK(studio.picked == said("HistoryTitle", args)); const std::vector listed = history->list(ALScriptStudioHistory::keyOf(doc)); + REQUIRE(listed.size() == 3U); CHECK(studio.candidates[0].label == ALRecoveryEntry::sayWhen(listed[0].when)); const std::string& newest = studio.candidates[0].detail; { @@ -209,8 +210,9 @@ namespace } { INFO("the first kept says nothing of one before"); - REQUIRE(studio.candidates[2].detail.find(said("HistorySameLength")) == std::string::npos); - REQUIRE(studio.candidates[2].detail.find("longer") == std::string::npos); + CHECK(studio.candidates[2].detail.find(said("HistorySameLength")) == std::string::npos); + CHECK(studio.candidates[2].detail.find("longer") == std::string::npos); + CHECK(studio.candidates[2].detail.find("shorter") == std::string::npos); } } @@ -223,6 +225,7 @@ namespace Doc& doc = tab("door", "the text now"); require_saved(doc, "the text then", 100.0); unit.show(doc); + REQUIRE(studio.candidates.size() == 1U); studio.choose(studio.candidates[0].value); UNSCOPED_INFO("compared"); REQUIRE(studio.did.size() == 1U); @@ -243,6 +246,7 @@ namespace Doc& loading = tab("lamp", "", false); require_saved(loading, "a lamp's text", 50.0); unit.show(loading); + REQUIRE(studio.candidates.size() == 1U); studio.choose(studio.candidates[0].value); UNSCOPED_INFO("not compared yet"); REQUIRE(studio.did.size() == 1U); @@ -321,8 +325,10 @@ namespace require_saved(doc, "second\nsame", 200.0); require_saved(doc, "third\nsame", 100.0); unit.show(doc); + REQUIRE(studio.candidates.size() == 3U); // Listed newest first: the middle one. studio.choose(studio.candidates[1].value); + REQUIRE(doc.compareView); ALDiffView& view = *doc.compareView; UNSCOPED_INFO("the slider"); CHECK(view.bar()->getChild("versions")->getVisible()); @@ -340,9 +346,11 @@ namespace CHECK(doc.historyShown->text == "third\nsame"); } const std::vector listed = history->list(ALScriptStudioHistory::keyOf(doc)); + REQUIRE(listed.size() == 3U); LLStringUtil::format_map_t when; when["[NAME]"] = "door"; when["[WHEN]"] = ALRecoveryEntry::sayWhen(listed[0].when); + REQUIRE(doc.compareTitles); UNSCOPED_INFO("titled by it"); CHECK(doc.compareTitles->theirs == said("HistorySavedAt", when)); { @@ -375,6 +383,7 @@ namespace require_saved(loading, "one", 200.0); require_saved(loading, "two", 100.0); unit.show(loading); + REQUIRE(studio.candidates.size() == 2U); studio.choose(studio.candidates[0].value); { INFO("waiting, with something to do once shown"); @@ -397,6 +406,7 @@ namespace Doc& alone = tab("gate", "now"); require_saved(alone, "only", 100.0); unit.show(alone); + REQUIRE(studio.candidates.size() == 1U); studio.choose(studio.candidates[0].value); { INFO("one save: no slider"); @@ -416,24 +426,29 @@ namespace require_saved(doc, "older", 200.0); require_saved(doc, "newer", 100.0); unit.show(doc); + REQUIRE(studio.candidates.size() == 2U); studio.choose(studio.candidates[0].value); + REQUIRE(doc.compareView); ALDiffView& view = *doc.compareView; UNSCOPED_INFO("at the newest"); CHECK(view.bar()->versionShown() == 1); // The older one gone from disk since it was listed. const std::vector listed = history->list(ALScriptStudioHistory::keyOf(doc)); + REQUIRE(listed.size() == 2U); std::error_code gone; std::filesystem::remove(fsyspath(listed[1].path), gone); ALViewType::as(view.bar()->getChild("older"))->press(); LLStringUtil::format_map_t args; args["[NAME]"] = "door"; args["[WHEN]"] = ALRecoveryEntry::sayWhen(listed[1].when); + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("HistoryUnreadable", args)); UNSCOPED_INFO("the slider put back"); CHECK(view.bar()->versionShown() == 1); { INFO("the left as it was"); CHECK(view.leftText() == "newer"); + REQUIRE(doc.historyShown); CHECK(doc.historyShown->text == "newer"); } } @@ -450,7 +465,9 @@ namespace require_saved(doc, "second", 200.0); require_saved(doc, "third", 100.0); unit.show(doc); + REQUIRE(studio.candidates.size() == 3U); studio.choose(studio.candidates[1].value); + REQUIRE(doc.compareView); ALDiffView& view = *doc.compareView; const auto press = [&view](const char* name) { ALViewType::as(view.bar()->getChild(name))->press(); }; press("older"); @@ -458,6 +475,7 @@ namespace CHECK(view.leftText() == std::string("first")); press("newer"); const std::vector listed = history->list(ALScriptStudioHistory::keyOf(doc)); + REQUIRE(listed.size() == 3U); std::error_code gone; std::filesystem::remove(fsyspath(listed[2].path), gone); press("older"); @@ -465,6 +483,7 @@ namespace INFO("the oldest again, as it was read"); CHECK(view.leftText() == "first"); CHECK(view.bar()->versionShown() == 0); + REQUIRE(doc.historyShown); CHECK(doc.historyShown->text == "first"); } } @@ -480,7 +499,9 @@ namespace require_saved(doc, "first", 300.0); require_saved(doc, "second", 200.0); unit.show(doc); + REQUIRE(studio.candidates.size() == 2U); studio.choose(studio.candidates[0].value); + REQUIRE(doc.compareView); ALDiffView& view = *doc.compareView; { INFO("the slider"); diff --git a/indra/newview/tests/alscriptstudiomasters_test.cpp b/indra/newview/tests/alscriptstudiomasters_test.cpp index b6e84bf597..a5b706fa2c 100644 --- a/indra/newview/tests/alscriptstudiomasters_test.cpp +++ b/indra/newview/tests/alscriptstudiomasters_test.cpp @@ -322,7 +322,12 @@ namespace return link; } - const al_studio_test::FakeServices::Said& said() const { return services.reports.back(); } + // The last thing said; there must be something. + const al_studio_test::FakeServices::Said& said() const + { + REQUIRE_FALSE(services.reports.empty()); + return services.reports.back(); + } static bool says(const std::string& text, const std::string& word) { return text.find(word) != std::string::npos; } typedef std::vector Names; }; @@ -420,8 +425,14 @@ namespace studio->picked = in(name); const size_t before = services.reports.size(); unit->linkToFile(doc); - return !disk.linkOf(doc.ref) && services.reports.size() == before + 1 && - alscriptstudiomasters_data::says(said().text, word) && said().failure && said().doc == doc.id && services.findDoc(doc.id) == &doc; + // Read straight, not through said(): the count checked first guards it, and an assertion in + // here would clear the message of the CHECK this is called in. + if (disk.linkOf(doc.ref) || services.reports.size() != before + 1) + { + return false; + } + const al_studio_test::FakeServices::Said& last = services.reports.back(); + return alscriptstudiomasters_data::says(last.text, word) && last.failure && last.doc == doc.id && services.findDoc(doc.id) == &doc; }; UNSCOPED_INFO("SLua for an LSL script"); CHECK(refused(lsl, "door.luau", "MasterNotLSLFile")); @@ -987,6 +998,7 @@ namespace { INFO("Switch to File: the file's tab in its place, said there"); REQUIRE_FALSE(services.findDoc(door_ref)); + REQUIRE_FALSE(studio->closed.empty()); CHECK(studio->closed.back() == "door"); CHECK(says(said().text, "MasterGaveWay")); CHECK(said().doc == "disk:" + in("door.lsl")); @@ -1004,6 +1016,7 @@ namespace { INFO("kept once clean"); REQUIRE(services.findDoc(lamp_ref) == &lamp); + REQUIRE(lamp.offer); CHECK(says(lamp.offer->text, "MasterLinkedClean")); } lamp.editor->setText("default {}\n"); diff --git a/indra/newview/tests/alscriptstudiomerging_test.cpp b/indra/newview/tests/alscriptstudiomerging_test.cpp index 98e3df496c..769daadc1c 100644 --- a/indra/newview/tests/alscriptstudiomerging_test.cpp +++ b/indra/newview/tests/alscriptstudiomerging_test.cpp @@ -168,12 +168,15 @@ namespace REQUIRE_FALSE(doc.savedThere); CHECK(studio.notices == 1); } + REQUIRE_FALSE(services().reports.empty()); CHECK(services().reports.back().text == services().counted("MergeConflicts", 1, named(doc))); // Settled from the comparison's bar: an edit of the tab. doc.compareView->right()->setFocus(true); doc.compareView->right()->goTo(ALTextPos(6, 0)); - ALViewType::as(doc.compareView->bar()->getChild("keep_both"))->press(); + ALFlatButton* keep_both = ALViewType::as(doc.compareView->bar()->findChild("keep_both")); + REQUIRE(keep_both); + keep_both->press(); UNSCOPED_INFO("mine then theirs"); CHECK(doc.editor->wholeText() == lines(9, { { 0, "theirs 0" }, { 2, "mine 2" }, { 6, "mine 6" }, { 7, "theirs 6" }, { 8, "line 7" } })); @@ -195,8 +198,10 @@ namespace doc.savedThere = lines(8, { { 0, "theirs 0" } }); unit->mergeSaved(doc); CHECK(doc.editor->wholeText() == lines(8, { { 0, "theirs 0" }, { 2, "mine 2" }, { 6, "mine 6" } })); + REQUIRE(doc.compareView); REQUIRE(doc.compareView->merging()); CHECK(doc.compareView->conflictCount() == 0); + REQUIRE_FALSE(services().reports.empty()); CHECK(services().reports.back().text == said("MergeClean", named(doc))); } @@ -223,6 +228,7 @@ namespace REQUIRE(doc.savedThere); CHECK(studio.notices == 0); } + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("MergeNoBase", named(doc))); Doc& locked = tab("lamp"); @@ -257,7 +263,9 @@ namespace CHECK(doc.editor->wholeText() == lines(8, { { 0, "theirs 0" }, { 2, "mine 2" }, { 6, "mine 6" } })); { INFO("under the world's title, as a merge"); + REQUIRE(studio.titles.size() == 1); CHECK(studio.titles.back() == said("CompareWorld")); + REQUIRE(doc.compareView); CHECK(doc.compareView->conflictCount() == 1); } UNSCOPED_INFO("made from what the world holds"); @@ -289,6 +297,7 @@ namespace REQUIRE_FALSE(doc.savedThere); REQUIRE(doc.merged); } + REQUIRE_FALSE(services().reports.empty()); UNSCOPED_INFO("the conflicts said with the comparison to show"); CHECK(services().reports.back().actions == std::vector{ "show_compare" }); // Typed in, or any change not a step back: nothing. @@ -357,6 +366,7 @@ namespace CHECK(doc.assetId == was); REQUIRE_FALSE(doc.merged); } + REQUIRE_FALSE(services().reports.empty()); UNSCOPED_INFO("reload, merge and compare offered again"); CHECK(services().reports.back().actions == std::vector{ "reload_world", "merge_world", "compare_world" }); } @@ -371,6 +381,7 @@ namespace doc.savedThere = theirs; unit->mergeSaved(doc); // As the window keeps what was said, and shows the comparison. + REQUIRE_FALSE(services().reports.empty()); doc.offer = Doc::Offer{ services().reports.back().text, services().reports.back().actions }; UNSCOPED_INFO("not while the comparison is not in front"); CHECK_FALSE(ALScriptStudioMerging::canSettle(doc)); @@ -382,6 +393,7 @@ namespace REQUIRE(notice.buttons[1].first.empty()); } + REQUIRE(doc.compareView); doc.compareView->right()->setFocus(true); doc.compareView->right()->goTo(ALTextPos(2, 0)); UNSCOPED_INFO("not where the caret is in no conflict"); @@ -422,6 +434,7 @@ namespace CHECK(doc.compareView->rightText() == doc.editor->wholeText()); REQUIRE(doc.compareView->merging()); CHECK(doc.compareView->conflictCount() == 0); + REQUIRE_FALSE(services().reports.empty()); CHECK(services().reports.back().text == said("MergeClean", named(doc))); } } diff --git a/indra/newview/tests/alscriptstudioorphans_test.cpp b/indra/newview/tests/alscriptstudioorphans_test.cpp index 1203f8fccc..c2fac1d300 100644 --- a/indra/newview/tests/alscriptstudioorphans_test.cpp +++ b/indra/newview/tests/alscriptstudioorphans_test.cpp @@ -258,6 +258,7 @@ namespace } { INFO("and said, with what can be done"); + REQUIRE_FALSE(services().reports.empty()); CHECK(services().reports.back().text == said("OrphanRemovedKept", "[NAME]", "b")); CHECK(services().reports.back().actions == Names{ "copy", "export" }); } @@ -417,10 +418,12 @@ namespace INFO("restored, the source in front"); CHECK(studio.did == Names{ "source a", "carried a: restored text" }); CHECK_FALSE(a.recoverable); + REQUIRE_FALSE(services().reports.empty()); CHECK(services().reports.back().text == said("RecoveryRestored", "[NAME]", "a")); } a.recoverable = ALRecoveryEntry(); unit.noticeAction("discard_left"); + REQUIRE_FALSE(studio.did.empty()); CHECK(studio.did.back() == "discard"); CHECK_FALSE(a.recoverable); a.recoverable = ALRecoveryEntry(); @@ -460,6 +463,7 @@ namespace a.orphan->kind = Orphan::Away; unit.refreshNotice(); LLView* holder = bar->getParent(); + REQUIRE(holder != nullptr); CHECK(holder->getVisible()); CHECK(bar->getChild("notice_text")->getText() == said("NoticeAway")); LLButton* first = bar->getChild("notice_first"); @@ -508,6 +512,7 @@ namespace } a.uploaded.text = std::make_shared("default { }"); unit.noticeAction("compare_compiled"); + REQUIRE_FALSE(studio.did.empty()); UNSCOPED_INFO("compared: as saved, beside what the source makes"); CHECK(studio.did.back() == "compare a: default { touch_start(integer n) { llDie(); } } | default { } (" + said("CompareCompiled") + " | " + said("CompareMade") + ")"); @@ -524,6 +529,7 @@ namespace CHECK(studio.did == Names{ "source a", "carried a: default { touch_start(integer n) { llDie(); } }" }); CHECK_FALSE(a.compiledDiffers); } + REQUIRE_FALSE(services().reports.empty()); UNSCOPED_INFO("and said"); CHECK(services().reports.back().text == said("CompiledTaken", "[NAME]", "a")); a.compiledDiffers = std::string("x"); @@ -531,6 +537,7 @@ namespace { INFO("kept: let go of, the next save replacing it"); CHECK_FALSE(a.compiledDiffers); + REQUIRE_FALSE(studio.did.empty()); CHECK(studio.did.back() == "source a"); } UNSCOPED_INFO("nothing more to say"); @@ -548,6 +555,7 @@ namespace UNSCOPED_INFO("offered as listed"); CHECK_FALSE(kept.whole); unit.noticeAction("compare_kept"); + REQUIRE_FALSE(studio.did.empty()); UNSCOPED_INFO("now beside kept"); CHECK(studio.did.back() == "compare a: default {} | default { state_entry() { } } (" + said("CompareNow") + " | " + said("CompareKept", "[WHEN]", kept.whenSaid()) + ")"); @@ -556,6 +564,7 @@ namespace unit.noticeAction("restore"); { INFO("the source in front first"); + REQUIRE(studio.did.size() >= 2); CHECK(studio.did[studio.did.size() - 2] == "source a"); CHECK(studio.did.back() == "carried a: default { state_entry() { } }"); } @@ -587,6 +596,7 @@ namespace unit.noticeAction("retry"); { INFO("taken up, and let go of"); + REQUIRE_FALSE(studio.did.empty()); CHECK(studio.did.back() == "offer a: retry"); CHECK_FALSE(a.offer); } @@ -719,6 +729,7 @@ namespace LLStringUtil::format_map_t args; args["[NAME]"] = "a"; args["[WHEN]"] = ALRecoveryEntry::sayWhen(shown.when); + REQUIRE_FALSE(services().reports.empty()); UNSCOPED_INFO("and said"); CHECK(services().reports.back().text == services().words("HistoryRestored", args)); diff --git a/indra/newview/tests/alscriptstudiorecovery_test.cpp b/indra/newview/tests/alscriptstudiorecovery_test.cpp index 93f723e8dc..6adde634cb 100644 --- a/indra/newview/tests/alscriptstudiorecovery_test.cpp +++ b/indra/newview/tests/alscriptstudiorecovery_test.cpp @@ -38,6 +38,7 @@ #include #include #include +#include namespace { @@ -452,7 +453,9 @@ namespace { INFO("a file there, opened and taken up"); REQUIRE(studio().activated.size() == 1); - REQUIRE(services().findDoc("disk:" + file.file)->carriedText); + const Doc* on_disk = services().findDoc("disk:" + file.file); + REQUIRE(on_disk != nullptr); + REQUIRE(on_disk->carriedText); } studio().inHand = false; @@ -480,7 +483,9 @@ namespace { INFO("its script opened, and the text to go in once loaded"); REQUIRE(services().opened.size() == 1); - CHECK(services().findDoc(ALScriptRef(entry.object, entry.item))->carriedText == std::optional("kept\n")); + const Doc* reopened = services().findDoc(ALScriptRef(entry.object, entry.item)); + REQUIRE(reopened != nullptr); + CHECK(reopened->carriedText == std::optional("kept\n")); } } @@ -497,9 +502,9 @@ namespace Doc& mine = tab("mine", "a\n"); type(mine, "b"); r.keep(mine); - Doc& theirs = tab("theirs", "c\n"); - const Entry left = require_left_for(theirs, "left\n"); - Doc& gone = tab("gone", "d\n"); + Doc& theirs = tab("theirs", "c\n"); + require_left_for(theirs, "left\n"); + Doc& gone = tab("gone", "d\n"); type(gone, "e"); CHECK(r.setAside(gone)); r.show(); @@ -536,7 +541,11 @@ namespace theirs.carriedText.reset(); theirs.loaded = false; + // The offer made afresh, not the one before still standing. + studio().candidates.clear(); r.show(); + UNSCOPED_INFO("the one thrown away, offered still"); + REQUIRE(studio().candidates.size() == 1); studio().chosen(studio().candidates.front().value); UNSCOPED_INFO("picked, taken up"); REQUIRE(theirs.carriedText.has_value()); @@ -603,8 +612,11 @@ namespace // Loaded clean, as a tab opened again is: what it keeps of its own // forgets nothing of that. r.keep(again); - UNSCOPED_INFO("still there to take"); - CHECK(std::filesystem::exists(fsyspath(again.recoverable->path))); + { + INFO("still there to take"); + REQUIRE(again.recoverable); + CHECK(std::filesystem::exists(fsyspath(again.recoverable->path))); + } const Entry offered = *again.recoverable; r.takeUp(again, offered); UNSCOPED_INFO("taken up"); @@ -754,7 +766,8 @@ namespace UNSCOPED_INFO("not another session's"); REQUIRE_FALSE(back); const std::vector listed = store->list(); - Entry read = listed.front(); + REQUIRE(listed.size() == 1); + Entry read = listed.front(); { INFO("read whole, the picks with it"); CHECK(store->load(read)); @@ -801,6 +814,12 @@ namespace std::vector taken; bool has = true; + // The windows' taker is the process's, and holds this case's own: + // let go of however this ends. + struct TakerBack + { + ~TakerBack() { ALRecovery::takeWindowsTo(nullptr); } + } taker_back; ALRecovery::takeWindowsTo([&taken, &has](const Entry& entry) { taken.push_back(entry.name); return has; @@ -831,7 +850,6 @@ namespace REQUIRE(taken.size() == 2); REQUIRE(studio().orphans.size() == 2); } - ALRecovery::takeWindowsTo(nullptr); } TEST_CASE_METHOD(alscriptstudiorecovery_data, "alscriptstudiorecovery: rekey lets go of the old key's entry and writes under the new one", "[alscriptstudiorecovery]") @@ -900,9 +918,26 @@ namespace UNSCOPED_INFO("the old item's moved"); REQUIRE(history->list("task:p:old").empty()); const std::vector moved = history->list("task:p:new"); - UNSCOPED_INFO("behind the new one's"); REQUIRE(moved.size() == 2U); + { + // Newest first: the new item's own save, then the old item's. + INFO("behind the new one's"); + ALSavedText newer = moved[0]; + ALSavedText older = moved[1]; + REQUIRE(history->load(newer)); + REQUIRE(history->load(older)); + CHECK(newer.text == "b"); + CHECK(older.text == "a"); + } + // Each item's saves are a folder of the history's. + const auto folders = [&folder]() { + std::error_code ec; + return std::distance(std::filesystem::directory_iterator(fsyspath(folder), ec), std::filesystem::directory_iterator()); + }; + const auto before = folders(); ALRecovery::keepSaved(save("", "nobody's", 5.0)); written(); + UNSCOPED_INFO("a save of nobody's kept nowhere"); + CHECK(folders() == before); } } diff --git a/indra/newview/tests/alscriptstudiosaving_test.cpp b/indra/newview/tests/alscriptstudiosaving_test.cpp index c41f2ac5fa..704d0489b5 100644 --- a/indra/newview/tests/alscriptstudiosaving_test.cpp +++ b/indra/newview/tests/alscriptstudiosaving_test.cpp @@ -338,6 +338,7 @@ namespace Doc& unchecked = tab("unchecked", "default {}"); saving.save(unchecked); + REQUIRE_FALSE(studio.tidied.empty()); UNSCOPED_INFO("no fixes over a text not checked"); CHECK(studio.tidied.back() == std::string("mt")); @@ -349,6 +350,7 @@ namespace // They changed the text: checked again before it goes. { INFO("checked again"); + REQUIRE_FALSE(studio.analysed.empty()); CHECK(studio.analysed.back() == "a"); CHECK(studio.sent.empty()); } @@ -387,7 +389,7 @@ namespace CHECK(doc.save.checked()); saving.save(doc); UNSCOPED_INFO("its errors stop it"); - CHECK(lastSaid() == std::string("PreflightErrors")); + REQUIRE(lastSaid() == std::string("PreflightErrors")); const al_studio_test::FakeServices::Said& said = services.reports.back(); { INFO("a failure, of the tab, with Save Anyway"); @@ -488,7 +490,7 @@ namespace Doc& big = tab("big", too_much); saving.save(big); UNSCOPED_INFO("too large: said"); - CHECK(lastSaid() == std::string("SaveTooLarge")); + REQUIRE(lastSaid() == std::string("SaveTooLarge")); UNSCOPED_INFO("with what would shrink it"); CHECK(services.reports.back().text.find("SaveTooLargePlain") != std::string::npos); { @@ -539,7 +541,7 @@ namespace UNSCOPED_INFO("sent all the same"); CHECK(studio.sent.size() == size_t(1)); UNSCOPED_INFO("said as it went"); - CHECK(lastSaid() == std::string("SavingWithErrors")); + REQUIRE(lastSaid() == std::string("SavingWithErrors")); { INFO("a failure of the tab, nothing to press"); CHECK(services.reports.back().failure); @@ -580,6 +582,7 @@ namespace REQUIRE(studio.runs.size() == 3); type(doc, "\n// b"); studio.answerRun(made); + REQUIRE_FALSE(studio.analysed.empty()); UNSCOPED_INFO("checked again"); CHECK(studio.analysed.back() == "a"); { @@ -708,7 +711,7 @@ namespace CHECK(services.findDoc(std::string_view("typed"))); } UNSCOPED_INFO("and said so"); - CHECK(lastSaid() == std::string("CopiedToKeptOpen")); + REQUIRE(lastSaid() == std::string("CopiedToKeptOpen")); UNSCOPED_INFO("said of the tab kept open"); CHECK(services.reports.back().doc == std::string("typed")); } @@ -923,6 +926,7 @@ namespace saving.save(doc); UNSCOPED_INFO("on its way"); CHECK(doc.save.sending()); + REQUIRE(studio.sent.size() == size_t(1)); const U64 mine = studio.sent.back().options.sender.request; { INFO("sent as a request of the studio's"); @@ -972,7 +976,7 @@ namespace type(dirty, "\n// mine"); saving.savedElsewhere(saved(dirty, "default {}\n// theirs", ALScriptOrigin::Bridge)); UNSCOPED_INFO("asked whose to keep"); - CHECK(lastSaid() == std::string("SavedElsewhereConflict")); + REQUIRE(lastSaid() == std::string("SavedElsewhereConflict")); UNSCOPED_INFO("with the four answers"); CHECK(services.reports.back().actions == Names{ "take_saved", "keep_saved", "merge_saved", "compare_saved" }); { @@ -1058,7 +1062,7 @@ namespace type(doc, "x"); saving.save(doc); UNSCOPED_INFO("moved: said"); - CHECK(lastSaid() == std::string("SaveWorldMoved")); + REQUIRE(lastSaid() == std::string("SaveWorldMoved")); UNSCOPED_INFO("with what can be done"); CHECK(services.reports.back().actions == Names{ "save_anyway", "reload_world", "merge_world", "compare_world" }); { diff --git a/indra/newview/tests/alscriptstudioselections_test.cpp b/indra/newview/tests/alscriptstudioselections_test.cpp index d2450aa493..123fe04137 100644 --- a/indra/newview/tests/alscriptstudioselections_test.cpp +++ b/indra/newview/tests/alscriptstudioselections_test.cpp @@ -160,13 +160,15 @@ namespace CHECK(unit->held()->title == titled("SelectionLines", "door", 3, 4)); LLStringUtil::format_map_t args; args["[TITLE]"] = unit->held()->title; + REQUIRE_FALSE(services().statuses.empty()); CHECK(services().statuses.back() == said("SelectionHeld", args)); door.editor->goTo(ALTextRange(ALTextPos(5, 0), ALTextPos(5, 4))); unit->hold(door); { INFO("one line, in place of the other"); - REQUIRE(unit->held()->text == "line"); + REQUIRE(unit->held().has_value()); + CHECK(unit->held()->text == "line"); CHECK(unit->held()->title == titled("SelectionLine", "door", 6)); } } @@ -214,6 +216,7 @@ namespace unit->hold(door); door.editor->goTo(ALTextRange(ALTextPos(5, 0), ALTextPos(7, 0))); unit->compare(door); + REQUIRE(door.compareView); ALDiffView& view = *door.compareView; UNSCOPED_INFO("its changes may be taken back"); CHECK(view.canTakeBack()); @@ -290,6 +293,8 @@ namespace CHECK(unit->canCompare(door)); } unit->compare(door); + // Compared, and so in its place: the window makes the view as it compares. + REQUIRE_FALSE(studio.did.empty()); UNSCOPED_INFO("its text"); CHECK(studio.did.back() == "line 0\nline 1\n | line 0\nexpanded 1\n (" + titled("SelectionLines", "door", 1, 2) + " | " + titled("SelectionLines", "door", 1, 2) + ")"); @@ -313,12 +318,14 @@ namespace unit->hold(door); door.editor->goTo(ALTextRange(ALTextPos(1, 12), ALTextPos(2, 9))); unit->compare(door); + REQUIRE(door.compareView); ALDiffView& view = *door.compareView; UNSCOPED_INFO("the stretch"); CHECK(view.rightText() == std::string("beta;\ninteger c")); view.right()->setFocus(true); view.right()->goTo(ALTextPos(0, 2)); view.right()->handleUnicodeChar('x', false); + REQUIRE_FALSE(studio.typedAt.empty()); UNSCOPED_INFO("on its first line, from where it starts"); CHECK(studio.typedAt.back() == ALTextPos(1, 14)); UNSCOPED_INFO("typed there"); diff --git a/indra/newview/tests/alscriptstudiovim_test.cpp b/indra/newview/tests/alscriptstudiovim_test.cpp index b37ba2a8e2..cc056f1214 100644 --- a/indra/newview/tests/alscriptstudiovim_test.cpp +++ b/indra/newview/tests/alscriptstudiovim_test.cpp @@ -377,6 +377,12 @@ namespace a.editor->insertText(" "); { INFO("wq over something unsaved saves it to close"); + CHECK(ex(a, "wq")); + CHECK(studio.savedToClose == Names{ "a" }); + } + studio.savedToClose.clear(); + { + INFO("and x the same"); CHECK(ex(a, "x")); CHECK(studio.savedToClose == Names{ "a" }); } @@ -420,11 +426,17 @@ namespace CHECK(ex(a, "set", "rnu")); CHECK(relativeNumbers); { - INFO("off already: left"); + INFO("on: turned off"); CHECK(ex(a, "set", "norelativenumber")); CHECK_FALSE(relativeNumbers); CHECK(ran.size() == 4); } + { + INFO("off already: left"); + CHECK(ex(a, "set", "nornu")); + CHECK_FALSE(relativeNumbers); + CHECK(ran.size() == 4); + } UNSCOPED_INFO("an option it does not know is vim's"); CHECK_FALSE(ex(a, "set", "hlsearch")); } @@ -626,6 +638,7 @@ namespace go("tabn", "9"); UNSCOPED_INFO("nor to go to"); CHECK(vim->message() == std::string("VimBadArgument")); + REQUIRE(services.frontDoc()); UNSCOPED_INFO("still where it was"); CHECK(services.frontDoc()->id == std::string("delta")); } @@ -661,6 +674,7 @@ namespace CHECK((go("tabm", "-1"), order()) == std::string("alpha beta gamma delta")); UNSCOPED_INFO(":tabm 3 after the third, past itself"); CHECK((go("tabm", "3"), order()) == std::string("alpha gamma beta delta")); + REQUIRE(services.frontDoc()); UNSCOPED_INFO("still in front"); CHECK(services.frontDoc()->id == std::string("beta")); go("tabm", "+5"); @@ -739,10 +753,12 @@ namespace CHECK(said->message() == std::string("VimArgumentRequired")); ex(a, "e", "beta"); + REQUIRE(services.frontDoc()); UNSCOPED_INFO("a tab by its name"); CHECK(services.frontDoc()->id == std::string("beta")); vim.pump(); ex(a, "e", "#"); + REQUIRE(services.frontDoc()); UNSCOPED_INFO("# the one before"); CHECK(services.frontDoc()->id == std::string("alpha")); Doc& main = tab("mainfile"); diff --git a/indra/newview/tests/alscriptstudioweighing_test.cpp b/indra/newview/tests/alscriptstudioweighing_test.cpp index 751d5ff9d3..3dabe20fb8 100644 --- a/indra/newview/tests/alscriptstudioweighing_test.cpp +++ b/indra/newview/tests/alscriptstudioweighing_test.cpp @@ -241,6 +241,7 @@ namespace { INFO("asked of what was sent, for its own target"); REQUIRE(studio.requests.size() == 1); + REQUIRE(studio.requests[0].text); CHECK(*studio.requests[0].text == "sent text"); CHECK(studio.requests[0].targets == std::vector{ Target::Mono }); } @@ -265,6 +266,9 @@ namespace } a.uploaded.disabled = true; unit.weighSent(a); + // Each question's answer is kept beside it: answers[1] below is there too. + REQUIRE(studio.requests.size() == 2); + REQUIRE(studio.requests[1].text); UNSCOPED_INFO("the preprocessor off: the text as written"); CHECK(*studio.requests[1].text == "default {}"); a.editor->setCaret(a.editor->document().end()); @@ -431,6 +435,7 @@ namespace result.version = versionOf(a); result.variantTotals = { 100, 90, 100 }; studio.answers[0](result); + REQUIRE(e.fixes().size() == 3); UNSCOPED_INFO("the lighter one said"); CHECK(e.fixes()[0].note.find("FixLighterEstimate") == 0); { diff --git a/indra/newview/tests/alscriptstudiowords_test.cpp b/indra/newview/tests/alscriptstudiowords_test.cpp index 378661423b..2481c0d5ef 100644 --- a/indra/newview/tests/alscriptstudiowords_test.cpp +++ b/indra/newview/tests/alscriptstudiowords_test.cpp @@ -110,6 +110,17 @@ namespace made->setText(text); return *made; } + // A word the definitions give, for a case to read: one missing ends + // the case here rather than reading through nothing. Asked before an + // UNSCOPED_INFO, not between it and its check, which this REQUIRE + // passing would take the message from. + static const Vocab& known(bool lua, const std::string& name) + { + const Vocab* found = Words::word(lua, name); + CAPTURE(name); + REQUIRE(found); + return *found; + } static std::vector texts(const std::vector& completions) { std::vector out; @@ -135,24 +146,30 @@ namespace } { INFO("and what returns"); - CHECK(Words::word(false, "llOld")->detail == "integer llOld()"); - CHECK(Words::word(false, "llOld")->deprecated); + const Vocab& old = known(false, "llOld"); + CHECK(old.detail == "integer llOld()"); + CHECK(old.deprecated); } + const Vocab& touch = known(false, "touch_start"); UNSCOPED_INFO("an event's"); - CHECK(Words::word(false, "touch_start")->detail == std::string("touch_start(integer num_detected)")); + CHECK(touch.detail == std::string("touch_start(integer num_detected)")); + const Vocab& pi = known(false, "PI"); UNSCOPED_INFO("a constant's type and value"); - CHECK(Words::word(false, "PI")->detail == std::string("float PI = 3.14159265")); + CHECK(pi.detail == std::string("float PI = 3.14159265")); + const Vocab& integer = known(false, "integer"); UNSCOPED_INFO("a type's tooltip's first line"); - CHECK(Words::word(false, "integer")->detail == std::string("A whole number.")); - CHECK(Words::word(false, "jump")->kind == ALSyntaxKind::Control); + CHECK(integer.detail == std::string("A whole number.")); + const Vocab& jump = known(false, "jump"); + CHECK(jump.kind == ALSyntaxKind::Control); { INFO("not a word"); CHECK_FALSE(Words::word(false, "x")); CHECK_FALSE(Words::word(false, "")); CHECK_FALSE(Words::word(false, "nothing")); } + const Vocab& slua_say = known(true, "ll.Say"); UNSCOPED_INFO("SLua's () returns nothing worth saying"); - CHECK(Words::word(true, "ll.Say")->detail == std::string("ll.Say(number channel)")); + CHECK(slua_say.detail == std::string("ll.Say(number channel)")); } TEST_CASE_METHOD(alscriptstudiowords_data, "alscriptstudiowords: the vocabulary is kept until forgotten and then built again", "[alscriptstudiowords]") @@ -224,22 +241,24 @@ namespace TEST_CASE_METHOD(alscriptstudiowords_data, "alscriptstudiowords: completions fill in handlers where they go and offer snippets", "[alscriptstudiowords]") { - const ALCodeEditor::Completion lsl = Words::completionFor(*Words::word(false, "touch_start"), false); + const ALCodeEditor::Completion lsl = Words::completionFor(known(false, "touch_start"), false); UNSCOPED_INFO("LSL's handler"); CHECK(lsl.snippet == std::string("touch_start(integer num_detected)\n{\n $0\n}")); - const ALCodeEditor::Completion slua = Words::completionFor(*Words::word(true, "touch_start"), true); + const Vocab& slua_touch = known(true, "touch_start"); + const ALCodeEditor::Completion slua = Words::completionFor(slua_touch, true); UNSCOPED_INFO("SLua's, a function of LLEvents'"); CHECK(slua.snippet == std::string("function LLEvents.touch_start(detected)\n $0\nend")); UNSCOPED_INFO("typed where asked, as the definitions type it"); - CHECK(Words::completionFor(*Words::word(true, "touch_start"), true, true).snippet == + CHECK(Words::completionFor(slua_touch, true, true).snippet == std::string("function LLEvents.touch_start(detected: {DetectedEvent})\n $0\nend")); + const Vocab& listen = known(true, "listen"); UNSCOPED_INFO("each parameter typed"); - CHECK(Words::completionFor(*Words::word(true, "listen"), true, true).snippet == + CHECK(Words::completionFor(listen, true, true).snippet == std::string("function LLEvents.listen(channel: number, name: string, id: uuid, msg: string)\n $0\nend")); UNSCOPED_INFO("each untyped"); - CHECK(Words::completionFor(*Words::word(true, "listen"), true).snippet == + CHECK(Words::completionFor(listen, true).snippet == std::string("function LLEvents.listen(channel, name, id, msg)\n $0\nend")); - const ALCodeEditor::Completion say = Words::completionFor(*Words::word(false, "llSay"), false); + const ALCodeEditor::Completion say = Words::completionFor(known(false, "llSay"), false); { INFO("a function as itself"); CHECK(say.snippet.empty()); @@ -332,8 +351,9 @@ namespace CHECK(Words::helpUrl(true, "LLEvents") == std::string("https://wiki.secondlife.com/wiki/Lua_Alpha")); UNSCOPED_INFO("nothing: the portal"); CHECK(Words::helpUrl(false, "") == std::string("https://wiki.example/LSL_Portal")); + const Vocab& pi = known(false, "PI"); UNSCOPED_INFO("the reference's words"); - CHECK(Words::referenceText(*Words::word(false, "PI"), false) == + CHECK(Words::referenceText(pi, false) == std::string("float PI = 3.14159265\n\nThe ratio.\nhttps://wiki.example/PI")); } @@ -517,20 +537,27 @@ namespace viewer.slots.definitionsYaml = [] { return std::string("functions:\n llSay:\n categories:\n - avatar_communication\n"); }; lsl["constants"]["PRIM_POSITION"] = LLSD().with("type", "integer").with("value", "6"); Words::forget(); + const Vocab& say = known(false, "llSay"); UNSCOPED_INFO("LSL, in words"); - CHECK(Words::word(false, "llSay")->group == std::string("Avatar communication")); + CHECK(say.group == std::string("Avatar communication")); + const Vocab& old = known(false, "llOld"); UNSCOPED_INFO("none where none is said"); - CHECK(Words::word(false, "llOld")->group.empty()); + CHECK(old.group.empty()); + const Vocab& touch = known(false, "touch_start"); UNSCOPED_INFO("an event none"); - CHECK(Words::word(false, "touch_start")->group.empty()); + CHECK(touch.group.empty()); + const Vocab& position = known(false, "PRIM_POSITION"); UNSCOPED_INFO("a constant by family"); - CHECK(Words::word(false, "PRIM_POSITION")->group == std::string("PRIM_...")); + CHECK(position.group == std::string("PRIM_...")); + const Vocab& pi = known(false, "PI"); UNSCOPED_INFO("one without a family none"); - CHECK(Words::word(false, "PI")->group.empty()); + CHECK(pi.group.empty()); + const Vocab& slua_say = known(true, "ll.Say"); UNSCOPED_INFO("SLua's ll function as its twin"); - CHECK(Words::word(true, "ll.Say")->group == std::string("Avatar communication")); + CHECK(slua_say.group == std::string("Avatar communication")); + const Vocab& math_pi = known(true, "math.pi"); UNSCOPED_INFO("another SLua function by library"); - CHECK(Words::word(true, "math.pi")->group == std::string("math")); + CHECK(math_pi.group == std::string("math")); } TEST_CASE_METHOD(alscriptstudiowords_data, "alscriptstudiowords: the units ask the attached viewer and default what it leaves unsaid", "[alscriptstudiowords]") @@ -620,21 +647,25 @@ namespace slua["events"]["varied"] = LLSD().with("arguments", LLSD().with(0, argument("first", "number")).with(1, argument("...", "any"))); slua["events"]["bare"] = LLSD().with("arguments", LLSD().with(0, argument("...any", ""))); Words::forget(); - const auto handler = [](const char* name, bool typed) { return Words::completionFor(*Words::word(true, name), true, typed).snippet; }; + const auto handler = [](const Vocab& word, bool typed) { return Words::completionFor(word, true, typed).snippet; }; + const Vocab& mapped = known(true, "mapped"); UNSCOPED_INFO("a generic's comma"); - CHECK(handler("mapped", true) == std::string("function LLEvents.mapped(pairs: Map, n: number)\n $0\nend")); + CHECK(handler(mapped, true) == std::string("function LLEvents.mapped(pairs: Map, n: number)\n $0\nend")); UNSCOPED_INFO("and untyped"); - CHECK(handler("mapped", false) == std::string("function LLEvents.mapped(pairs, n)\n $0\nend")); + CHECK(handler(mapped, false) == std::string("function LLEvents.mapped(pairs, n)\n $0\nend")); + const Vocab& called = known(true, "called"); UNSCOPED_INFO("a function type's arrow, a table type's colon"); - CHECK(handler("called", true) == + CHECK(handler(called, true) == std::string("function LLEvents.called(back: (number) -> (), data: {[string]: any})\n $0\nend")); + const Vocab& varied = known(true, "varied"); UNSCOPED_INFO("a variadic typed"); - CHECK(handler("varied", true) == std::string("function LLEvents.varied(first: number, ...: any)\n $0\nend")); + CHECK(handler(varied, true) == std::string("function LLEvents.varied(first: number, ...: any)\n $0\nend")); UNSCOPED_INFO("and not"); - CHECK(handler("varied", false) == std::string("function LLEvents.varied(first, ...)\n $0\nend")); + CHECK(handler(varied, false) == std::string("function LLEvents.varied(first, ...)\n $0\nend")); + const Vocab& bare = known(true, "bare"); UNSCOPED_INFO("one written ...any"); - CHECK(handler("bare", true) == std::string("function LLEvents.bare(...: any)\n $0\nend")); + CHECK(handler(bare, true) == std::string("function LLEvents.bare(...: any)\n $0\nend")); UNSCOPED_INFO("and untyped"); - CHECK(handler("bare", false) == std::string("function LLEvents.bare(...)\n $0\nend")); + CHECK(handler(bare, false) == std::string("function LLEvents.bare(...)\n $0\nend")); } } diff --git a/indra/newview/tests/alscripttypes_test.cpp b/indra/newview/tests/alscripttypes_test.cpp index 95f9f63ff9..13e169fdd1 100644 --- a/indra/newview/tests/alscripttypes_test.cpp +++ b/indra/newview/tests/alscripttypes_test.cpp @@ -120,6 +120,7 @@ TEST_CASE_METHOD(alscripttypes_data, "alscripttypes: inSource maps diagnostics b const std::vector wrapped = result.inSource(1); { INFO("a line down in an envelope, no column"); + REQUIRE(wrapped.size() == size_t(4)); CHECK(wrapped[0].hasLine); CHECK(wrapped[0].line == 4); CHECK_FALSE(wrapped[0].hasColumn); diff --git a/indra/newview/tests/alscriptweightspane_test.cpp b/indra/newview/tests/alscriptweightspane_test.cpp index 11aec4dba9..86e7f432f1 100644 --- a/indra/newview/tests/alscriptweightspane_test.cpp +++ b/indra/newview/tests/alscriptweightspane_test.cpp @@ -62,7 +62,21 @@ namespace REQUIRE(pane != nullptr); return *pane; } - ALPaneList* list(const char* name) const { return floater ? floater->findChild(name, true) : nullptr; } + // One of the tab's lists, by its name in the skin. + ALPaneList* require_list(const char* name) const + { + ALPaneList* found = floater ? floater->findChild(name, true) : nullptr; + CAPTURE(name); + REQUIRE(found != nullptr); + return found; + } + // The head over the lists, which says whose weights they are. + LLTextBox* require_head() const + { + LLTextBox* head = floater ? floater->findChild("weights_head", true) : nullptr; + REQUIRE(head != nullptr); + return head; + } static std::string cell(const LLScrollListItem* item, S32 column) { @@ -148,8 +162,8 @@ namespace REQUIRE(tab() != nullptr); ALScriptWeightsPane& pane = require_pane(); pane.show(lsl()); - ALPaneList* targets = list("weights_targets"); - ALPaneList* parts = list("weights_parts"); + ALPaneList* targets = require_list("weights_targets"); + ALPaneList* parts = require_list("weights_parts"); REQUIRE(targets->getItemCount() == 3); UNSCOPED_INFO("its own first"); CHECK(cell(targets->getAllData()[0], 0) == std::string("LSL (LSO)")); @@ -178,7 +192,7 @@ namespace CHECK(std::find(lines.begin(), lines.end(), "lib.lsl:1") != lines.end()); UNSCOPED_INFO("the script's own at its line" + joined(lines)); CHECK(std::find(lines.begin(), lines.end(), "8") != lines.end()); - const std::string head = floater->findChild("weights_head", true)->getText(); + const std::string head = require_head()->getText(); { INFO("the head says whose and on what: " + head); CHECK(head.find("Door") != std::string::npos); @@ -196,8 +210,8 @@ namespace } ALScriptWeightsPane& pane = require_pane(); pane.show(lsl()); - ALPaneList* targets = list("weights_targets"); - ALPaneList* parts = list("weights_parts"); + ALPaneList* targets = require_list("weights_targets"); + ALPaneList* parts = require_list("weights_parts"); parts->selectByValue(LLSD(2)); std::optional place = pane.chosenPlace(parts); @@ -246,8 +260,10 @@ namespace saved.parts.erase(saved.parts.begin() + 2); shown.saved = { saved }; pane.show(shown); - ALPaneList* targets = list("weights_targets"); - ALPaneList* parts = list("weights_parts"); + ALPaneList* targets = require_list("weights_targets"); + ALPaneList* parts = require_list("weights_parts"); + REQUIRE(targets->getItemCount() == 3); + REQUIRE(parts->getItemCount() == 5); UNSCOPED_INFO("the target's whole"); CHECK(cell(targets->getAllData()[0], 3) == std::string("+96")); UNSCOPED_INFO("nothing for a target not weighed then"); @@ -291,10 +307,13 @@ namespace ALScriptWeightsPane& pane = require_pane(); pane.show(lsl()); pane.showNothing("Nothing open to weigh."); - CHECK(list("weights_targets")->getItemCount() == 0); - CHECK(list("weights_parts")->getItemCount() == 0); + ALPaneList* targets = require_list("weights_targets"); + ALPaneList* parts = require_list("weights_parts"); + LLTextBox* head = require_head(); + CHECK(targets->getItemCount() == 0); + CHECK(parts->getItemCount() == 0); CHECK_FALSE(pane.chosenPlace(pane.partsList())); - CHECK(floater->findChild("weights_head", true)->getText() == std::string("Nothing open to weigh.")); + CHECK(head->getText() == std::string("Nothing open to weigh.")); UNSCOPED_INFO("and whose it is forgotten"); CHECK(pane.shownId().empty()); } @@ -321,7 +340,7 @@ namespace shown.weights = { slua }; shown.saved = { slua }; pane.show(shown); - std::vector changes = column(list("weights_parts"), 4); + std::vector changes = column(require_list("weights_parts"), 4); UNSCOPED_INFO("nothing changed"); CHECK(joined(changes) == std::string("|||")); // The middle one heavier, and one added after them. @@ -330,7 +349,7 @@ namespace now.parts.push_back(part(Kind::Function, "", 40, 12)); shown.weights = { now }; pane.show(shown); - changes = column(list("weights_parts"), 4); + changes = column(require_list("weights_parts"), 4); UNSCOPED_INFO("the middle one, and one new"); CHECK(joined(changes) == std::string("||+50||new")); } @@ -355,7 +374,7 @@ namespace shown.weights = { slua }; shown.handlers[0] = "touch_start"; pane.show(shown); - const std::string names = joined(column(list("weights_parts"), 0)); + const std::string names = joined(column(require_list("weights_parts"), 0)); UNSCOPED_INFO("the handler by its event: " + names); CHECK(names.find("touch_start") != std::string::npos); UNSCOPED_INFO("the other with none: " + names); @@ -374,7 +393,7 @@ namespace } ALScriptWeightsPane& pane = require_pane(); ALScriptWeightsPane::Shown shown = lsl(); - LLTextBox* head = floater->findChild("weights_head", true); + LLTextBox* head = require_head(); pane.show(shown); UNSCOPED_INFO("nothing of the region"); CHECK(head->getText().find("region") == std::string::npos); @@ -449,11 +468,11 @@ namespace shown.weights[2].total = 2048; std::swap(shown.weights[0], shown.weights[2]); pane.show(shown); - const std::string luau = floater->findChild("weights_head", true)->getText(); + const std::string luau = require_head()->getText(); UNSCOPED_INFO("said for LSL on Luau: " + luau); CHECK(luau.find("records no lines") != std::string::npos); pane.show(lsl()); - const std::string lso = floater->findChild("weights_head", true)->getText(); + const std::string lso = require_head()->getText(); UNSCOPED_INFO("not for LSO: " + lso); CHECK(lso.find("records no lines") == std::string::npos); } @@ -498,12 +517,9 @@ namespace shown.weights = { slua }; pane.show(shown); - ALPaneList* strings = list("weights_strings"); - { - INFO("the list there"); - REQUIRE(strings); - CHECK(strings->getParent()->getVisible()); - } + ALPaneList* strings = require_list("weights_strings"); + UNSCOPED_INFO("the list there"); + CHECK(strings->getParent()->getVisible()); UNSCOPED_INFO("a start, then each string"); REQUIRE(strings->getItemCount() == 5); const std::vector texts = column(strings, 0); @@ -540,8 +556,9 @@ namespace REQUIRE(place); CHECK(place->line == 2); } + ALPaneList* parts = require_list("weights_parts"); UNSCOPED_INFO("not the parts' choice"); - CHECK_FALSE(pane.chosenPlace(list("weights_parts"))); + CHECK_FALSE(pane.chosenPlace(parts)); // Kept chosen through a refill, by what it is. strings->selectByValue(LLSD(1)); @@ -585,8 +602,10 @@ namespace kept = start; of = texts; }); + LLButton* keep = floater->findChild("weights_keep_start", true); + REQUIRE(keep); strings->selectByValue(LLSD(0)); - floater->findChild("weights_keep_start", true)->onCommit(); + keep->onCommit(); { INFO("asked with the start and its strings"); CHECK(kept == "You have touched me, "); @@ -595,13 +614,13 @@ namespace } kept.clear(); strings->selectByValue(LLSD(1)); - floater->findChild("weights_keep_start", true)->onCommit(); + keep->onCommit(); UNSCOPED_INFO("not for a string"); CHECK(kept.empty()); // Nor in a script that cannot be written to. pane.setKeepStart([&](const std::string& start, const std::vector&) { kept = start; }, []() { return false; }); strings->selectByValue(LLSD(0)); - floater->findChild("weights_keep_start", true)->onCommit(); + keep->onCommit(); UNSCOPED_INFO("not where the script cannot be written to"); CHECK(kept.empty()); diff --git a/indra/newview/tests/alselectionoutline_gl_test.cpp b/indra/newview/tests/alselectionoutline_gl_test.cpp index 295e8f8388..d7e8109dd1 100644 --- a/indra/newview/tests/alselectionoutline_gl_test.cpp +++ b/indra/newview/tests/alselectionoutline_gl_test.cpp @@ -1333,7 +1333,7 @@ namespace if (idAt(ids, x, y) == ID_A) { ++covered; - hidden += at(ids, x, y).b == 0 ? 1 : 0; + hidden += at(ids, x, y).b != 255 ? 1 : 0; } } } @@ -2205,7 +2205,6 @@ namespace // read as the sum of its coverage down a column, the line's width over the cosine of its slope. TEST_CASE_METHOD(alselectionoutline_data, "ALSelectionOutline: the wireframe's lines are as wide in the window wherever they are", "[ALSelectionOutline]") { - // The rows a column's runs of drawn pixels cover, bottom up. // The width down column x of the quad's diagonal, the middle of the three lines a column crosses. auto diagonal = [&](const std::vector& px, S32 x) { @@ -2237,10 +2236,12 @@ namespace REQUIRE(fabsf(far_end - near_end) < 0.1f); const std::vector far_px = worldWireframe({ { quad(-4.f, 6.f, -4.f, 4.f, -12.f, -48.f), ID_A } }); + const F32 farther_near_end = diagonal(far_px, 26); + const F32 farther_far_end = diagonal(far_px, 34); { INFO("the same four times as far"); - REQUIRE(fabsf(diagonal(far_px, 26) - near_end) < 0.1f); - REQUIRE(fabsf(diagonal(far_px, 34) - far_end) < 0.1f); + REQUIRE(fabsf(farther_near_end - near_end) < 0.1f); + REQUIRE(fabsf(farther_far_end - far_end) < 0.1f); } const std::vector wide_px = worldWireframe({ { quad(-1.f, 1.5f, -1.f, 1.f, -3.f, -12.f), ID_A } }, 2); @@ -2254,7 +2255,8 @@ namespace } } - // Across a line, coverage falls from its middle on either side, and is gone within a pixel and a half. + // Across a line, coverage falls from its middle on either side, and along a row is gone within a pixel and a + // half. TEST_CASE_METHOD(alselectionoutline_data, "ALSelectionOutline: a line's coverage falls from its middle to nothing on either side", "[ALSelectionOutline]") { for (bool reverse : conventions()) @@ -2272,6 +2274,7 @@ namespace REQUIRE(at(px, y, y).r > 127); for (S32 step : { -1, 1 }) { + CAPTURE(step); U32 last = at(px, y, y).r; for (S32 x = y + step; x != y + 4 * step; x += step) { @@ -2280,8 +2283,8 @@ namespace REQUIRE(r <= last); last = r; } - UNSCOPED_INFO("and is gone"); - REQUIRE(last == 0U); + UNSCOPED_INFO("and is gone two pixels along the row"); + REQUIRE(at(px, y + 2 * step, y).r == 0U); } } } diff --git a/indra/newview/tests/alterrainsurfacemaps_test.cpp b/indra/newview/tests/alterrainsurfacemaps_test.cpp index 52e0cb6b1a..553e40512c 100644 --- a/indra/newview/tests/alterrainsurfacemaps_test.cpp +++ b/indra/newview/tests/alterrainsurfacemaps_test.cpp @@ -31,6 +31,7 @@ #include "../alterrainsurfacemaps.h" +#include #include #include @@ -60,8 +61,25 @@ namespace TEST_CASE_METHOD(alterrainsurfacemaps_data, "ALTerrainSurfaceMaps: fill() lays the grid out APRON in with rows along y", "[ALTerrainSurfaceMaps]") { std::vector out; - ALTerrainSurfaceMaps::fill(out, GPE, 1, [](S32 gx, S32 gy, F32* t) { t[0] = (F32)(gx * 100 + gy); }); + // How many times each coordinate on the map is visited, and how many + // visits are off it. + std::vector visits(static_cast(SIZE) * SIZE, 0); + S32 off_map = 0; + ALTerrainSurfaceMaps::fill(out, GPE, 1, [&visits, &off_map](S32 gx, S32 gy, F32* t) { + t[0] = (F32)(gx * 100 + gy); + if (gx < -APRON || gx >= GPE + APRON || gy < -APRON || gy >= GPE + APRON) + { + ++off_map; + return; + } + ++visits[texel(gx, gy)]; + }); REQUIRE(out.size() == static_cast(SIZE) * SIZE); + { + INFO("every coordinate visited once, and none off the map"); + CHECK(off_map == 0); + CHECK(std::count_if(visits.begin(), visits.end(), [](S32 n) { return n != 1; }) == 0); + } UNSCOPED_INFO("first texel is the south-west apron corner"); CHECK(out[texel(-APRON, -APRON)] == (F32)(-APRON * 100 - APRON)); UNSCOPED_INFO("grid origin sits APRON in"); diff --git a/indra/newview/tests/altexturetable_test.cpp b/indra/newview/tests/altexturetable_test.cpp index 88194be953..ead002d5e3 100644 --- a/indra/newview/tests/altexturetable_test.cpp +++ b/indra/newview/tests/altexturetable_test.cpp @@ -408,15 +408,30 @@ namespace REQUIRE(((i % 3 == 0) ? found == nullptr : found == items[i - 1])); } + // The same inserts and erases with a reservation and without: the + // same items, in the same places. Table reserved; reserved.reserve(count); + Table unreserved; for (U32 i = 1; i <= 100; ++i) { CAPTURE(i); REQUIRE(reserved.insert(key(i), new Item(i))); + REQUIRE(unreserved.insert(key(i), new Item(i))); } CHECK(reserved.size() == (size_t)100); + for (U32 i = 3; i <= 100; i += 3) + { + reserved.erase(key(i)); + unreserved.erase(key(i)); + } CHECK(reserved.consistent()); + REQUIRE(reserved.size() == unreserved.size()); + for (size_t i = 0; i < reserved.size(); ++i) + { + CAPTURE(i); + REQUIRE(reserved[i]->mId == unreserved[i]->mId); + } } // --- find-or-insert ------------------------------------------------------ diff --git a/indra/newview/tests/alwhitebalancesolver_test.cpp b/indra/newview/tests/alwhitebalancesolver_test.cpp index 7422ad4d66..67c68ba437 100644 --- a/indra/newview/tests/alwhitebalancesolver_test.cpp +++ b/indra/newview/tests/alwhitebalancesolver_test.cpp @@ -42,9 +42,9 @@ namespace TEST_CASE_METHOD(wb_data, "ALWhiteBalanceSolver: neutral in gives a gain of one", "[ALWhiteBalanceSolver]") { const LLVector3 g = ALWhiteBalanceSolver::gain(0.f, 0.f); - CHECK_THAT(g.mV[VX], ll_test::WithinBits(1.f, 6)); - CHECK_THAT(g.mV[VY], ll_test::WithinBits(1.f, 6)); - CHECK_THAT(g.mV[VZ], ll_test::WithinBits(1.f, 6)); + CHECK(g.mV[VX] == 1.f); + CHECK(g.mV[VY] == 1.f); + CHECK(g.mV[VZ] == 1.f); } // Green is pinned across the whole range, which is what makes temperature diff --git a/indra/newview/tests/cppfeatures_test.cpp b/indra/newview/tests/cppfeatures_test.cpp index a256a6dfb7..f6559d6cfe 100644 --- a/indra/newview/tests/cppfeatures_test.cpp +++ b/indra/newview/tests/cppfeatures_test.cpp @@ -31,6 +31,8 @@ #include "linden_common.h" #include +#include + namespace { @@ -130,7 +132,8 @@ TEST_CASE("LLCPPFeatures: range for walks a container or a C array", "[LLCPPFeat class Foo { public: - virtual bool is_happy() const = 0; + // Not happy itself, so that a happy answer through a Foo is Bar's. + virtual bool is_happy() const { return false; } }; class Bar: public Foo @@ -146,8 +149,10 @@ class Bar: public Foo TEST_CASE("LLCPPFeatures: an override specifier marks a virtual override", "[LLCPPFeatures]") { + // Asked through its base, which only an override answers. Bar b; - CHECK(b.is_happy()); + const Foo& as_foo = b; + CHECK(as_foo.is_happy()); } // final @@ -160,7 +165,9 @@ TEST_CASE("LLCPPFeatures: an override specifier marks a virtual override", "[LLC class Vehicle { public: - virtual bool has_wheels() const = 0; + // No wheels itself, so that wheels through a Vehicle are the final + // override's. + virtual bool has_wheels() const { return false; } }; class WheeledVehicle: public Vehicle @@ -179,8 +186,11 @@ class Bicycle: public WheeledVehicle TEST_CASE("LLCPPFeatures: a class derives from one with a final override", "[LLCPPFeatures]") { + // Asked through the root, a Bicycle answers with the final override it + // inherits. Bicycle bi; - CHECK(bi.has_wheels()); + const Vehicle& as_vehicle = bi; + CHECK(as_vehicle.has_wheels()); } // deleted function declaration @@ -206,11 +216,14 @@ class DoNotCopy }; -TEST_CASE("LLCPPFeatures: a deleted copy constructor leaves default construction", "[LLCPPFeatures]") +// Default construction is checked by compiling; the copy is asked of the +// type, since a copy written out would not compile. +TEST_CASE("LLCPPFeatures: a deleted copy constructor forbids copies but not default construction", "[LLCPPFeatures]") { DoNotCopy nc; // OK, default constructor //DoNotCopy nc2(nc); // No, can't copy //DoNotCopy nc3 = nc; // No, this also calls copy constructor (even though it looks like an assignment) + CHECK_FALSE(std::is_copy_constructible_v); } // defaulted function declaration @@ -321,15 +334,15 @@ TEST_CASE("LLCPPFeatures: constexpr functions compute values", "[LLCPPFeatures]" // // You can add asserts to be checked at compile time. The thing to be checked must be a constexpr. // There are two forms: -// * static_assert(expr); +// * static_assert(expr); (since C++17) // * static_assert(expr, message); // -// Currently only the 2-parameter form works on windows. The 1-parameter form needs a flag we don't set. +// The case's checks are the compiler's: it passes by compiling, and then by returning. TEST_CASE("LLCPPFeatures: static_assert checks a constexpr at compile time", "[LLCPPFeatures]") { - // static_assert(ce_factorial(6)==720); No, needs a flag we don't currently set. - static_assert(ce_factorial(6)==720, "bad factorial"); // OK + static_assert(ce_factorial(6)==720); + static_assert(ce_factorial(6)==720, "bad factorial"); } // type aliases diff --git a/indra/newview/tests/llagentaccess_test.cpp b/indra/newview/tests/llagentaccess_test.cpp index 4fd2c2ca93..0defc92ccc 100644 --- a/indra/newview/tests/llagentaccess_test.cpp +++ b/indra/newview/tests/llagentaccess_test.cpp @@ -244,12 +244,15 @@ namespace #endif // HACKED_GODLIKE_VIEWER // make sure that even if pref is high, if access is low we block access - // this shouldn't occur in real life but we want to be safe - cgr.setU32("PreferredMaturity", SIM_ACCESS_ADULT); + // this shouldn't occur in real life but we want to be safe. The + // preference goes after the rating, which setMaturity would pull it + // down to. aa.setMaturity('P'); + cgr.setU32("PreferredMaturity", SIM_ACCESS_ADULT); #ifndef HACKED_GODLIKE_VIEWER { INFO("pref adult, actual pg"); + CHECK(aa.prefersAdult()); CHECK(aa.wantsPGOnly()); CHECK_FALSE(aa.canAccessMature()); CHECK_FALSE(aa.canAccessAdult()); diff --git a/indra/newview/tests/llhttpretrypolicy_test.cpp b/indra/newview/tests/llhttpretrypolicy_test.cpp index 2ed768cb56..a50025f547 100644 --- a/indra/newview/tests/llhttpretrypolicy_test.cpp +++ b/indra/newview/tests/llhttpretrypolicy_test.cpp @@ -243,8 +243,6 @@ TEST_CASE("retry_policy: getSecondsUntilRetryAfter parses seconds and an HTTP da TEST_CASE("retry_policy: retry-after is honoured in both llmessage and CoreHttp headers", "[retry_policy]") { - std::cerr << "7 starts" << std::endl; - LLSD sd_headers; time_t nowseconds; time(&nowseconds); @@ -261,7 +259,8 @@ TEST_CASE("retry_policy: retry-after is honoured in both llmessage and CoreHttp CHECK(should_retry); CHECK_THAT(seconds_to_wait, ll_test::WithinBits(17.0F, 6)); - // retry header should override, give delay of 0 + // A retry-after date 7 seconds off overrides the default: a delay of + // about 7 seconds. std::string date_string = LLDate((F64)(nowseconds+7)).asRFC1123(); sd_headers[HTTP_IN_HEADER_RETRY_AFTER] = date_string; policy.onFailure(503,sd_headers); diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 4c02155661..2f3671686a 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -50,12 +50,6 @@ // Constants const std::string VIEWERLOGIN_URI("viewerlogin_uri"); -const std::string VIEWERLOGIN_GRIDLABEL("viewerlogin_grid"); - -const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno"); - -const std::string VIEWERLOGIN_CHANNEL("invalid_channel"); -const std::string VIEWERLOGIN_VERSION("invalid_version"); // Link seams. @@ -259,16 +253,7 @@ namespace { class MockNotifications : public LLNotificationsInterface { - std::function mResponder; - int mAddedCount; - public: - MockNotifications() : - mResponder(0), - mAddedCount(0) - { - } - virtual ~MockNotifications() {} /* virtual */ LLNotificationPtr add( @@ -277,36 +262,8 @@ class MockNotifications : public LLNotificationsInterface const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor) { - mResponder = functor; - mAddedCount++; return LLNotificationPtr((LLNotification*)NULL); } - - void sendYesResponse() - { - LLSD notification; - LLSD response; - response = 1; - mResponder(notification, response); - } - - void sendNoResponse() - { - LLSD notification; - LLSD response; - response = 2; - mResponder(notification, response); - } - - void sendBogusResponse() - { - LLSD notification; - LLSD response; - response = 666; - mResponder(notification, response); - } - - int addedCount() { return mAddedCount; } }; } // anonymous namespace diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index 76bdad4bca..c438dc6458 100644 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -88,17 +88,23 @@ namespace CHECK(observer->mProcessed); } - // CHOP-220: a dangling observer pointer. - TEST_CASE_METHOD(RemoteParcelRequestData, "LLRemoteParcelRequest: a reply after its observer is deleted does not crash", "[LLRemoteParcelRequest]") + // CHOP-220: a dangling observer pointer. The deleted observer comes + // before a live one for the same parcel: the reply passes it over, does + // not crash on it, and still reaches the live one. + TEST_CASE_METHOD(RemoteParcelRequestData, "LLRemoteParcelRequest: a reply after an observer is deleted passes it over and reaches the rest", "[LLRemoteParcelRequest]") { LLRemoteParcelInfoObserver * observer = new TestObserver(); + TestObserver live; LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); processor.addObserver(LLUUID(TEST_PARCEL_ID), observer); + processor.addObserver(LLUUID(TEST_PARCEL_ID), &live); delete observer; observer = NULL; processor.processParcelData(testParcelData()); + CHECK(live.mProcessed); + processor.removeObserver(LLUUID(TEST_PARCEL_ID), &live); } } diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index 34bba3d616..090cbc080b 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -946,7 +946,7 @@ namespace UNSCOPED_INFO("load a known credential: authenticator"); CHECK_THAT(my_authenticator, ll_test::LLSDEquals(my_new_cred->getAuthenticator())); UNSCOPED_INFO("load a known credential: grid"); - CHECK(my_cred->getGrid() == "my_grid"); + CHECK(my_new_cred->getGrid() == "my_grid"); // test deletion of a credential handler->deleteCredential(my_new_cred); @@ -954,9 +954,9 @@ namespace UNSCOPED_INFO("delete credential: identifier"); REQUIRE(my_new_cred->getIdentifier().isUndefined()); UNSCOPED_INFO("delete credential: authenticator"); - REQUIRE(my_new_cred->getIdentifier().isUndefined()); + REQUIRE(my_new_cred->getAuthenticator().isUndefined()); UNSCOPED_INFO("delete credential: grid"); - CHECK(my_cred->getGrid() == "my_grid"); + CHECK(my_new_cred->getGrid() == "my_grid"); // load unknown cred my_new_cred = handler->loadCredential("my_grid"); @@ -1404,7 +1404,7 @@ namespace // trusted test_chain->add(new LLBasicCertificate(mX509ChildCert, &mValidationDate)); - // validate without the trust flag. + // validate with the trust flag: the store's cache of validated certs vouches for the child. test_store->validate(VALIDATION_POLICY_TRUSTED, test_chain, validation_params); // Validate with child cert but no parent, and no parent in CA store @@ -1429,7 +1429,7 @@ namespace LLCertValidationTrustException, (*test_chain)[1], test_store->validate, - VALIDATION_POLICY_TRUSTED | VALIDATION_POLICY_TRUSTED, + VALIDATION_POLICY_TRUSTED, test_chain, validation_params); // validate without the trust flag @@ -1443,8 +1443,11 @@ namespace test_store->validate(VALIDATION_POLICY_TIME | VALIDATION_POLICY_TRUSTED, test_chain, validation_params); + // valid from its first second validation_params = LLSD::emptyMap(); validation_params[CERT_VALIDATION_DATE] = child_info[CERT_VALID_FROM].asDate(); + CHECK_NOTHROW(test_store->validate(VALIDATION_POLICY_TIME | VALIDATION_POLICY_TRUSTED, + test_chain, validation_params)); validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() - 1.0); diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp index e53340a1fc..30cbbd92cd 100644 --- a/indra/newview/tests/llslurl_test.cpp +++ b/indra/newview/tests/llslurl_test.cpp @@ -340,8 +340,8 @@ namespace { INFO("secondlife:///secondlife/ slurl naming no grid"); CHECK(slurl.getType() == LLSLURL::LOCATION); - CHECK(slurl.getType() == LLSLURL::LOCATION); CHECK("myregion" == slurl.getRegion()); + CHECK(slurl.getPosition() == LLVector3(1, 2, 3)); CHECK(slurl.getGrid() == "util.agni.lindenlab.com"); } @@ -349,8 +349,8 @@ namespace { INFO("secondlife://Aditi/secondlife/ slurl"); CHECK(slurl.getType() == LLSLURL::LOCATION); - CHECK(slurl.getType() == LLSLURL::LOCATION); CHECK("myregion" == slurl.getRegion()); + CHECK(slurl.getPosition() == LLVector3(1, 2, 3)); CHECK(slurl.getGrid() == "Aditi"); } diff --git a/indra/newview/tests/lltextureinfo_test.cpp b/indra/newview/tests/lltextureinfo_test.cpp index 3bbc085b68..805ac9ddbd 100644 --- a/indra/newview/tests/lltextureinfo_test.cpp +++ b/indra/newview/tests/lltextureinfo_test.cpp @@ -37,6 +37,7 @@ #include "../llmeshrepository.h" #include "../lltexturecache.h" #include "../lltexturefetch.h" +#include "../llviewercontrol.h" #include "../llvocache.h" // Tut header @@ -91,6 +92,40 @@ LLVOCache::~LLVOCache() {} namespace { + // Texture downloads logged to the simulator, which is what getAverages() + // reports on, as a person would set it, and put back when the case ends, + // a failed check included. The amount a bundle of statistics is sent at + // is set past what any case here downloads: sending reaches for the + // texture fetcher and cache, which this test has none of. + struct LoggedToSimulator + { + LoggedToSimulator() + { + if (!gSavedSettings.controlExists("LogTextureDownloadsToSimulator")) + { + gSavedSettings.declareBOOL("LogTextureDownloadsToSimulator", false, "Declared In Code", LLControlVariable::PERSIST_NO); + } + if (!gSavedSettings.controlExists("TextureLoggingThreshold")) + { + gSavedSettings.declareU32("TextureLoggingThreshold", 1, "Declared In Code", LLControlVariable::PERSIST_NO); + } + mLoggedWas = gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"); + mThresholdWas = gSavedSettings.getU32("TextureLoggingThreshold"); + gSavedSettings.setU32("TextureLoggingThreshold", 1000000); + gSavedSettings.setBOOL("LogTextureDownloadsToSimulator", true); + } + ~LoggedToSimulator() + { + gSavedSettings.setBOOL("LogTextureDownloadsToSimulator", mLoggedWas); + gSavedSettings.setU32("TextureLoggingThreshold", mThresholdWas); + } + LoggedToSimulator(const LoggedToSimulator&) = delete; + LoggedToSimulator& operator=(const LoggedToSimulator&) = delete; + + bool mLoggedWas = false; + U32 mThresholdWas = 1; + }; + // Test wrapper declarations struct textureinfo_test { @@ -115,11 +150,12 @@ namespace // Test the LLTextureInfo // --------------------------------------------------------------------------------------- + // Made, set to log, and destroyed: there is nothing to ask of it, and the + // case passes by returning. TEST_CASE_METHOD(textureinfo_test, "LLTectureInfo: an LLTextureInfo is made with logging on", "[LLTectureInfo]") { LLTextureInfo tex_info; tex_info.setLogging(true); - CHECK(true); } TEST_CASE_METHOD(textureinfo_test, "LLTectureInfo: has() is false for a UUID never added", "[LLTectureInfo]") @@ -204,52 +240,54 @@ namespace CHECK(tex_info.getRequestOffset(id) == 1234); } + // What the averages are made of is what was logged to the simulator, in + // the texture info's own recording, as the texture fetcher keeps it. TEST_CASE_METHOD(textureinfo_test, "LLTectureInfo: getAverages gives the bit rate and bytes and transport", "[LLTectureInfo]") { - SKIP("Test skipped due to use of LLTrace stats failing in test"); - + LoggedToSimulator logged; LLTextureInfo tex_info; tex_info.setLogging(true); + tex_info.startRecording(); - S32 requestStartTimeOne = 200; - U64Microseconds requestEndTimeOne(400); + // A kilobyte in a second, in microseconds. + U64 requestStartTimeOne = 1000000; + U64Microseconds requestEndTimeOne(2000000); S32 requestSizeOne = 1024; - S32 requestSizeOneBits = requestSizeOne * 8; LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); tex_info.setRequestStartTime(id1, requestStartTimeOne); tex_info.setRequestSize(id1, requestSizeOne); tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); - U32 requestStartTimeTwo = 100; - U64Microseconds requestEndTimeTwo(500); + // Two kilobytes in two seconds. + U64 requestStartTimeTwo = 1000000; + U64Microseconds requestEndTimeTwo(3000000); U32 requestSizeTwo = 2048; - S32 requestSizeTwoBits = requestSizeTwo * 8; LLUUID id2("10e65d70-46fd-429f-841a-bf698e9424d4"); tex_info.setRequestStartTime(id2, requestStartTimeTwo); tex_info.setRequestSize(id2, requestSizeTwo); tex_info.setRequestType(id2, LLTextureInfoDetails::REQUEST_TYPE_HTTP); tex_info.setRequestCompleteTimeAndLog(id2, requestEndTimeTwo); - S32 averageBitRate = ((requestSizeOneBits / (S32(requestEndTimeOne.value()) - requestStartTimeOne)) + - (requestSizeTwoBits / (S32(requestEndTimeTwo.value()) - requestStartTimeTwo))) / - 2; - - S32 totalBytes = requestSizeOne + requestSizeTwo; - + // 3072 bytes, 24576 bits, over three seconds of downloading: 8192 + // bits a second, which is each download's rate too, so the rate does + // not hang on how the two are averaged. LLSD results = tex_info.getAverages(); - CHECK(results["bits_per_second"].asInteger() == averageBitRate); - CHECK(results["bytes_downloaded"].asInteger() == totalBytes); + CHECK(results["bits_per_second"].asInteger() == 8192); + CHECK(results["bytes_downloaded"].asInteger() == 3072); CHECK(results["transport"].asString() == std::string("HTTP")); } TEST_CASE_METHOD(textureinfo_test, "LLTectureInfo: resetTextureStatistics clears the averages", "[LLTectureInfo]") { + LoggedToSimulator logged; LLTextureInfo tex_info; tex_info.setLogging(true); + tex_info.startRecording(); - S32 requestStartTimeOne = 200; - U64Microseconds requestEndTimeOne(400); + // A kilobyte in a second, in microseconds. + U64 requestStartTimeOne = 1000000; + U64Microseconds requestEndTimeOne(2000000); S32 requestSizeOne = 1024; LLUUID id1("10e65d70-46fd-429f-841a-bf698e9424d3"); tex_info.setRequestStartTime(id1, requestStartTimeOne); @@ -257,7 +295,13 @@ namespace tex_info.setRequestType(id1, LLTextureInfoDetails::REQUEST_TYPE_HTTP); tex_info.setRequestCompleteTimeAndLog(id1, requestEndTimeOne); - tex_info.getAverages(); + LLSD before = tex_info.getAverages(); + { + INFO("something to clear"); + CHECK(before["bits_per_second"].asInteger() == 8192); + CHECK(before["bytes_downloaded"].asInteger() == 1024); + CHECK(before["transport"].asString() == std::string("HTTP")); + } tex_info.resetTextureStatistics(); LLSD results = tex_info.getAverages(); CHECK(results["bits_per_second"].asInteger() == 0); diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index 2c1354b94a..2543d27d3a 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -130,8 +130,6 @@ static const LLUUID region1("4e2d81a3-6263-6ffe-ad5c-8ce04bee07e8"); static const LLUUID region2("68762cc8-b68b-4e45-854b-e830734f2d4a"); static const U64 region1_handle(0x0000040000003f00ULL); static const U64 region2_handle(0x0000030000004200ULL); -static const std::string region1_handle_str("0000040000003f00"); -static const std::string region2_handle_str("0000030000004200"); #if 0 static bool @@ -277,6 +275,11 @@ namespace ~tst_viewerassetstats_index() { + // The global stats a case made, where a failed REQUIRE stopped it + // before it deleted them, are not left for the cases after it; + // deleted while the case's recorder is still there. + delete gViewerAssetStats; + gViewerAssetStats = nullptr; LLTrace::set_master_thread_recorder(NULL); } @@ -349,7 +352,7 @@ namespace } } - TEST_CASE_METHOD(tst_viewerassetstats_index, "tst_viewerassetstats_test: a new non-global instance's counts start at zero", "[tst_viewerassetstats_test]") + TEST_CASE_METHOD(tst_viewerassetstats_index, "tst_viewerassetstats_test: a new non-global instance's counts and response times start at zero", "[tst_viewerassetstats_test]") { LLViewerAssetStats * it = new LLViewerAssetStats(); it->setRegion(region1_handle); @@ -357,7 +360,7 @@ namespace LLSD sd = it->asLLSD(false); CHECK(is_double_key_map(sd, "regions", "duration")); CHECK(is_single_slot_array(sd["regions"], region1_handle)); - sd = sd[0]; + sd = sd["regions"][0]; delete it; @@ -392,7 +395,9 @@ namespace // Reset and check zeros... // Reset leaves current region in place gViewerAssetStats->reset(); - sd = gViewerAssetStats->asLLSD(false)["regions"][region1_handle_str]; + sd = gViewerAssetStats->asLLSD(false); + CHECK(is_single_slot_array(sd["regions"], region1_handle)); + sd = sd["regions"][0]; delete gViewerAssetStats; gViewerAssetStats = NULL; diff --git a/indra/newview/tests/llviewercontrollistener_test.cpp b/indra/newview/tests/llviewercontrollistener_test.cpp index 43af275f4a..c698bc2332 100644 --- a/indra/newview/tests/llviewercontrollistener_test.cpp +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -48,11 +48,8 @@ namespace } }; - TEST_CASE_METHOD(llviewercontrollistener_data, "llviewercontrollistener: CommonControl with no listener is left untested", "[llviewercontrollistener]") - { - // Not implemented: the linker drags in LLViewerControlListener when - // we bring in LLViewerControl. - } + // CommonControl with no listener is not tested: the linker drags in + // LLViewerControlListener when we bring in LLViewerControl. TEST_CASE_METHOD(llviewercontrollistener_data, "llviewercontrollistener: CommonControl::get of an unknown group names the group", "[llviewercontrollistener]") { diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index 5ab4347a6e..42c922af4b 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -98,7 +98,12 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, namespace { - TEST_CASE("LLViewerHelpUtil: buildHelpURL substitutes the topic and the viewer tags into the format", "[LLViewerHelpUtil]") + // What buildHelpURL does itself: the format read from HelpURLFormat, and + // the topic made safe for a URL and handed to the expansion as [TOPIC]. + // The viewer's own tags ([LANGUAGE], [VERSION], [OS] and the rest) are + // LLWeb::expandURLSubstitutions's, which this test stubs, so they are not + // tested here. + TEST_CASE("LLViewerHelpUtil: buildHelpURL expands the format from the settings with the topic encoded", "[LLViewerHelpUtil]") { std::string topic("test_topic"); std::string subresult; @@ -118,43 +123,9 @@ namespace UNSCOPED_INFO("topic name"); CHECK(subresult == "test_topic"); - gHelpURL = "[LANGUAGE]"; - gLanguage = ""; - subresult = LLViewerHelpUtil::buildHelpURL(topic); - UNSCOPED_INFO("simple substitution with blank"); - CHECK(subresult == ""); - - gHelpURL = "[LANGUAGE]"; - gLanguage = "Esperanto"; - subresult = LLViewerHelpUtil::buildHelpURL(topic); - UNSCOPED_INFO("simple substitution"); - CHECK(subresult == "Esperanto"); - - gHelpURL = "http://secondlife.com/[LANGUAGE]"; - gLanguage = "Gaelic"; - subresult = LLViewerHelpUtil::buildHelpURL(topic); - UNSCOPED_INFO("simple substitution with url"); - CHECK(subresult == "http://secondlife.com/Gaelic"); - - gHelpURL = "[XXX]"; - subresult = LLViewerHelpUtil::buildHelpURL(topic); - UNSCOPED_INFO("unknown substitution"); - CHECK(subresult == "[XXX]"); - - gHelpURL = "[LANGUAGE]/[LANGUAGE]"; - gLanguage = "Esperanto"; - subresult = LLViewerHelpUtil::buildHelpURL(topic); - UNSCOPED_INFO("multiple substitution"); - CHECK(subresult == "Esperanto/Esperanto"); - - gHelpURL = "http://[CHANNEL]/[VERSION]/[LANGUAGE]/[OS]/[GRID]/[XXX]"; - gChannel = "Second Life Test"; - gVersion = "2.0"; - gLanguage = "gaelic"; - gOS = "AmigaOS 2.1"; - gGrid = "mysim"; - subresult = LLViewerHelpUtil::buildHelpURL(topic); - UNSCOPED_INFO("complex substitution"); - CHECK(subresult == "http://Second Life Test/2.0/gaelic/AmigaOS 2.1/mysim/[XXX]"); + gHelpURL = "http://help/[TOPIC]"; + subresult = LLViewerHelpUtil::buildHelpURL("two words/a~b"); + UNSCOPED_INFO("all but letters, digits, '-', '.' and '_' escaped, the tilde too"); + CHECK(subresult == "http://help/two%20words%2Fa%7Eb"); } } diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index db2a97725f..bcf8558eb4 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -348,7 +348,7 @@ namespace // validate grid selection TEST_CASE_METHOD(viewerNetworkTest, "LLViewerNetwork: setGridChoice selects the grid the getters answer for", "[LLViewerNetwork]") { - // adding a grid with simply a name will populate the values. + // The sample grid file, for a grid besides Agni and Aditi to choose. llofstream gridfile(TEST_FILENAME); gridfile << gSampleGridFile; gridfile.close(); diff --git a/indra/newview/tests/llvocache_test.cpp b/indra/newview/tests/llvocache_test.cpp index d1c4fb8856..2f286df188 100644 --- a/indra/newview/tests/llvocache_test.cpp +++ b/indra/newview/tests/llvocache_test.cpp @@ -509,6 +509,8 @@ namespace UNSCOPED_INFO("version line"); CHECK(bytes.substr(0, line_end) == LLGLTFOverrideCacheEntry::VERSION_LABEL + ":" + std::to_string(LLGLTFOverrideCacheEntry::VERSION)); size_t cursor = line_end + 1 + UUID_BYTES; + UNSCOPED_INFO("the cache id, the count and the first record's length inside the file"); + REQUIRE(cursor + 2 * sizeof(U32) <= bytes.size()); U32 count = 0; memcpy(&count, bytes.data() + cursor, sizeof(U32)); cursor += sizeof(U32); @@ -597,19 +599,15 @@ namespace UNSCOPED_INFO("past the cap is still a factor"); CHECK(factor(2.f * cap, 65536.f, cap, 750.f, 2048.f) == 0.f); - // settings the wrong way round + // settings the wrong way round: the low one is the bound, and the high + // one is taken as just past it for (F32 allocated : { 0.f, 2000.f, 2500.f, 3000.f, 4000.f }) { const F32 f = factor(allocated, 0.f, no_cap, 3000.f, 2048.f); - CAPTURE(allocated); - { - INFO("inverted settings still give a factor in [0, 1]"); - REQUIRE(f >= 0.f); - REQUIRE(f <= 1.f); - } + CAPTURE(allocated, f); + INFO("inverted settings: up to the low setting nothing is pulled in, past it everything is"); + CHECK(close_to(f, allocated <= 3000.f ? 1.f : 0.f)); } - UNSCOPED_INFO("inverted settings: below the low setting nothing is pulled in"); - CHECK(close_to(factor(2000.f, 0.f, no_cap, 3000.f, 2048.f), 1.f)); // a bound set by hand is the bound, tighter than RAM would make it too auto explicit_factor = [](F32 allocated, F32 physical, F32 cap, F32 low, F32 high) @@ -622,8 +620,10 @@ namespace CHECK(close_to(explicit_factor(2000.f, 65536.f, no_cap, 1000.f, 2000.f), 0.f)); UNSCOPED_INFO("explicit bounds on a big machine: halfway is half"); CHECK(close_to(explicit_factor(1500.f, 65536.f, no_cap, 1000.f, 2000.f), 0.5f)); - UNSCOPED_INFO("an explicit high with a default low still comes out ordered"); - CHECK(LLVOCacheEntry::memoryAdjustFactor(16000.f, 65536.f, no_cap, 750.f, 2048.f, false, true) >= 0.f); + // The default low follows RAM to 16384, past the explicit high of + // 2048, so the high is taken as just past the low. + UNSCOPED_INFO("an explicit high with a default low still comes out ordered: below the low one nothing is pulled in"); + CHECK(close_to(LLVOCacheEntry::memoryAdjustFactor(16000.f, 65536.f, no_cap, 750.f, 2048.f, false, true), 1.f)); } TEST_CASE_METHOD(vocacheTest, "LLVOCache: hostile extras records meet a depth cap and a budget and lose bad faces", "[LLVOCache]") diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index 76a04d4e8f..eb959ae8ef 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -349,7 +349,7 @@ namespace // Test the LLWorldMap interface // --------------------------------------------------------------------------------------- // Test Setters and Accessors methods - TEST_CASE_METHOD(worldmap_test, "LLWorldMap: the image and region calls do not throw and no tile is loaded", "[LLWorldMap]") + TEST_CASE_METHOD(worldmap_test, "LLWorldMap: the image and region calls do not throw", "[LLWorldMap]") { // Test 1 : reset() try { @@ -387,10 +387,10 @@ namespace } catch (...) { FAIL("LLWorldMap::equalizeBoostLevels() threw"); } - // Test 7 : getObjectsTile() + // Test 7 : getObjectsTile(), which the mipmap answers: a stub here, + // whose tile says nothing of the map's. try { - LLPointer image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1); - CHECK(image.isNull()); + mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1); } catch (...) { FAIL("LLWorldMap::getObjectsTile() threw"); } diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp index e3cc6f2237..f8d6ec4f5d 100644 --- a/indra/newview/tests/llworldmipmap_test.cpp +++ b/indra/newview/tests/llworldmipmap_test.cpp @@ -108,11 +108,12 @@ namespace CHECK(grid_x == 0); CHECK(grid_y == 0); } - TEST_CASE_METHOD(worldmipmap_test, "LLWorldMipmap: getObjectsTile (not yet tested)", "[LLWorldMipmap]") + TEST_CASE_METHOD(worldmipmap_test, "LLWorldMipmap: getObjectsTile without loading gives nothing for a tile not loaded", "[LLWorldMipmap]") { - // Depends on some inline methods in LLViewerImage... Thinking about how to make this work - // LLPointer img = mMap->getObjectsTile(0, 0, 1); - // ensure("getObjectsTile() test failed", img.isNull()); + // Loading is not tested: it boosts the texture the texture manager + // fetches, and this binary's stub fetches none. + LLPointer img = mMap->getObjectsTile(0, 0, 1, false); + CHECK(img.isNull()); } TEST_CASE_METHOD(worldmipmap_test, "LLWorldMipmap: equalizeBoostLevels does not throw", "[LLWorldMipmap]") { diff --git a/indra/newview/tests/lutcube_test.cpp b/indra/newview/tests/lutcube_test.cpp index ee8d42590a..09386e7441 100644 --- a/indra/newview/tests/lutcube_test.cpp +++ b/indra/newview/tests/lutcube_test.cpp @@ -157,6 +157,7 @@ namespace } const LutCube cube = parse(text.str()); + REQUIRE(cube.size == 2); UNSCOPED_INFO("above domain clamps to white"); CHECK(at(cube, 0, 0) == FULL); UNSCOPED_INFO("below domain clamps to black");