mission_seed_world_change_test.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. // Regression for the selected-state arrival window.
  2. //
  3. // An authored region is `sliceSetIndex + stateOrdinal`. Ember's apex gameplay and both ending
  4. // bookends are sibling states of slice set 0 (regions 0, 1 and 2), while every earlier area is
  5. // its own slice set. Only a slice-set change replaces the client's instantiated world.
  6. //
  7. // Treating a sibling move as a replacement is what froze the mission's ending: the roster
  8. // withheld region 1's groups until the client reported holding region 1, and the client's
  9. // current region leg only advances on a real slice-set switch, so the report never came.
  10. #include <cassert>
  11. #include <cstdint>
  12. #include "../Sunrise/src/server/bap/encrypted/push/activity/mission_seed_world_change.h"
  13. namespace {
  14. namespace seed = sunrise::server::bap::encrypted::push::activity;
  15. /** Ember's authored states, as `{sliceSetIndex, effectiveRegion}`. */
  16. constexpr std::uint32_t kApexSliceSet = 0;
  17. constexpr std::uint32_t kApexRegion = 0;
  18. constexpr std::uint32_t kFirstMovieRegion = 1;
  19. constexpr std::uint32_t kSecondMovieRegion = 2;
  20. constexpr std::uint32_t kArrivalSliceSet = 48;
  21. constexpr std::uint32_t kArrivalRegion = 49;
  22. constexpr std::uint32_t kPowerhouseSliceSet = 64;
  23. constexpr std::uint32_t kPowerhouseRegion = 64;
  24. constexpr std::uint32_t kLinkSliceSet = 56;
  25. constexpr std::uint32_t kLinkRegion = 56;
  26. constexpr std::uint32_t kCinderSliceSet = 40;
  27. constexpr std::uint32_t kCinderRegion = 40;
  28. void sibling_states_keep_their_world() {
  29. // Apex gameplay to the first ending bookend: same slice set, so nothing is rebuilt.
  30. assert(!seed::mission_seed_region_change_replaces_world(
  31. kApexSliceSet, kApexRegion, kApexSliceSet, kFirstMovieRegion));
  32. // First bookend to the second: still slice set 0.
  33. assert(!seed::mission_seed_region_change_replaces_world(
  34. kApexSliceSet, kFirstMovieRegion, kApexSliceSet, kSecondMovieRegion));
  35. // And back, so a checkpoint or reselection is treated the same way.
  36. assert(!seed::mission_seed_region_change_replaces_world(
  37. kApexSliceSet, kSecondMovieRegion, kApexSliceSet, kApexRegion));
  38. }
  39. void slice_set_changes_replace_the_world() {
  40. // Every transition the mission already made is a real slice-set change and must stay one,
  41. // or the roster would register the new region's groups into a world being torn down.
  42. assert(seed::mission_seed_region_change_replaces_world(
  43. kArrivalSliceSet, kArrivalRegion, kPowerhouseSliceSet, kPowerhouseRegion));
  44. assert(seed::mission_seed_region_change_replaces_world(
  45. kPowerhouseSliceSet, kPowerhouseRegion, kLinkSliceSet, kLinkRegion));
  46. assert(seed::mission_seed_region_change_replaces_world(
  47. kLinkSliceSet, kLinkRegion, kCinderSliceSet, kCinderRegion));
  48. assert(seed::mission_seed_region_change_replaces_world(
  49. kCinderSliceSet, kCinderRegion, kApexSliceSet, kApexRegion));
  50. // Backtracking is a replacement in both directions.
  51. assert(seed::mission_seed_region_change_replaces_world(
  52. kCinderSliceSet, kCinderRegion, kLinkSliceSet, kLinkRegion));
  53. }
  54. void reselecting_the_same_state_is_never_a_replacement() {
  55. assert(!seed::mission_seed_region_change_replaces_world(
  56. kApexSliceSet, kApexRegion, kApexSliceSet, kApexRegion));
  57. assert(!seed::mission_seed_region_change_replaces_world(
  58. kCinderSliceSet, kCinderRegion, kCinderSliceSet, kCinderRegion));
  59. // A same-region selection stays a no-op even if the slice sets disagree: the region is the
  60. // publication key, and re-registering identical groups must not reopen an arrival window.
  61. assert(!seed::mission_seed_region_change_replaces_world(
  62. kApexSliceSet, kApexRegion, kCinderSliceSet, kApexRegion));
  63. }
  64. void decision_is_available_at_compile_time() {
  65. // The production caller is in a lock-held path, so the predicate must fold away.
  66. static_assert(!seed::mission_seed_region_change_replaces_world(0, 0, 0, 1));
  67. static_assert(seed::mission_seed_region_change_replaces_world(40, 40, 0, 0));
  68. }
  69. void arrival_window_closes_when_no_arrival_can_be_reported() {
  70. constexpr std::uint32_t factor = 8;
  71. // Ember's ending: apex gameplay held, region 1 pending, both slice set 0. No slice-set switch
  72. // happens, so the client never reports region 1 and the window must close on its own.
  73. assert(seed::mission_seed_arrival_window_closed(kApexRegion, kFirstMovieRegion,
  74. kApexSliceSet, factor));
  75. assert(seed::mission_seed_arrival_window_closed(kFirstMovieRegion, kSecondMovieRegion,
  76. kApexSliceSet, factor));
  77. // The ordinary case still closes on the arrival itself.
  78. assert(seed::mission_seed_arrival_window_closed(kPowerhouseRegion, kPowerhouseRegion,
  79. kPowerhouseSliceSet, factor));
  80. }
  81. void arrival_window_stays_open_across_a_real_slice_set_change() {
  82. constexpr std::uint32_t factor = 8;
  83. // Holding Cinder while Apex is pending is a genuine teardown: the window must stay open or
  84. // the publication registers the new region's groups into the world being torn down.
  85. assert(!seed::mission_seed_arrival_window_closed(kCinderRegion, kApexRegion,
  86. kApexSliceSet, factor));
  87. assert(!seed::mission_seed_arrival_window_closed(kPowerhouseRegion, kLinkRegion,
  88. kLinkSliceSet, factor));
  89. // Holding nothing yet is not an arrival.
  90. assert(!seed::mission_seed_arrival_window_closed(-1, kApexRegion, kApexSliceSet, factor));
  91. // A zero factor would make the slice-set test meaningless; it must not close the window.
  92. assert(!seed::mission_seed_arrival_window_closed(kApexRegion, kFirstMovieRegion,
  93. kApexSliceSet, 0));
  94. }
  95. } // namespace
  96. int main() {
  97. sibling_states_keep_their_world();
  98. slice_set_changes_replace_the_world();
  99. reselecting_the_same_state_is_never_a_replacement();
  100. decision_is_available_at_compile_time();
  101. arrival_window_closes_when_no_arrival_can_be_reported();
  102. arrival_window_stays_open_across_a_real_slice_set_change();
  103. return 0;
  104. }