Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix miscoordination between CiviUnitTestCase/CiviEnvBuilder. Fix flaky ConformanceTest. #25177

Merged
merged 4 commits into from
Dec 16, 2022

Conversation

totten
Copy link
Member

@totten totten commented Dec 15, 2022

Overview

(Replaces #25159. Follow-up to #24739.)

This fixes a subtle and general leak in how CiviUnitTestCase and CiviEnvBuilder coordinate the use of the headless DB. It was discovered while investigating a flaky test-result that stems from 5.57's #24739 -- specifically, there is a spooky interaction between CRM_Contribute_Import_Parser_ContributionTest and api\v4\Entity\ConformanceTest -- where the outcome of ConformanceTest appears to change arbitrarily.

After fixing the general leak, the spooky-interaction goes away, and the tests behave reliably. However, the reliable behavior is to complain. So I also update ConformanceTest -- resolving the complaint, removing an adhoc override, and getting it to pass.

(I suspect that developers have been locally+intermittently bitten by this spooky interaction in the past -- just not in a way that was visible to CI. So I'm including a bit more of write-up to explain what was fixed.)

General: Background

Recall that CiviUnitTestCase and CiviEnvBuilder provide two different implementations of a similar concept.

  • Basic Concept: When running tests, you need to setup the DB. You should do it regularly and reproducibly, but you shouldn't do it for every test-method (because it's slow/expensive). You should have some rules to decide (a) how to setup the DB and (b) when to reset it.

  • CiviUnitTestCase: The older implementation. Tightly coupled. Only allows one specific baseline. Mingled with a lot of unrelated functionality inCiviUnitTestCase.

  • CiviEnvBuilder: The newer implementation. Loosely coupled. Allows different baselines. Can be mixed into any plain-old TestCase. Used by Civi\Test::headless() and Civi\Test::e2e().

    • When you first callCiviEnvBuilder, it resets the DB and stores a flag (civitests_rev) that says "Here is how I setup the DB." Each time you call again, it checks that flag and decides whether the current environment is good enough. (If not, then it resets.)

The problem arises when you interleave test-runs that are based on the two systems. This could be a structural thing (one large test-suite with heterogeneous tests), or it could be circumstantial -- where a developer or testbot happens to run tests in a disagreeable order.

General: Before

When running interleaved tests, it misses some resets.

  1. Run some tests based on CiviEnvBuilder
    • This resets the DB and also stores a DB signature. ("Here is how we setup the DB...")
  2. Run some tests based on CiviUnitTestCase
    • This resets the DB, but leaves the old DB signature in place.
  3. Run some tests based on CiviEnvBuilder
    • This sees the old DB signature and wrongly concludes that we still have the DB from step (1). It does not reset.

General: After

  1. Run some tests based on CiviEnvBuilder
    • This resets the DB and also stores a DB signature. ("Here is how we setup the DB...")
  2. Run some tests based on CiviUnitTestCase
    • This resets the DB and also stores a DB signature. ("Here is how we setup the DB...")
  3. Run some tests based on CiviEnvBuilder
    • This resets the DB and also stores a DB signature. ("Here is how we setup the DB...")

Specific: Before

CRM_Contribute_Import_Parser_ContributionTest (based on CiviUnitTestCase) enables a series of several extensions, including search_kit. Prior to #24739, it cleaned up after itself (disabling+uninstalling each extension -- returning to CiviUnitTestCase's baseline environment). However, #24739 made it impossible to directly disable search_kit. Thus, ContributionTest left SK active.

ConformanceTest (based on CiviEnvBuilder) sometimes runs after ContributionTest (as in CiviCRM-Core-PR) -- which means SK is sometimes active. But it sometimes runs independently (as in CiviCRM-Core-Matrix) -- which means that SK is sometimes inactive.

There is an override in ConformanceTest. If you keep it, then CiviCRM-Core-Matrix fails. If you remove it, then CiviCRM-Core-PR fails.

Specifics: After

It doesn't matter if you run ConformanceTest via CiviCRM-Core-PR or CiviCRM-Core-Matrix -- they both start from the same baseline (ie SK disabled).

The override can be removed.

Specifics: Future

Given that policy has changed (search_kit is becoming mandatory), it may be appealing to change the test-baselines -- so that CiviUnitTestCase and CiviEnvBuilder both include it. Right now, I'm mostly concerned about getting a stable signal from the tests. Changes to baseline should be pursued as a separate exercise.

Tweaking the `$name` is theoretically useful if you have entirely
independent data-sets that happen to live in the same DB.  In practice, we
only use CiviEnvBuilder to manage Civi-related data-sets.
Background
----------

Recall that `CiviUnitTestCase` and `CiviEnvBuilder` are two different
implementations of a similar concept.

* `Concept`: If two tests use the same baseline DB/environment, and if they
  preserve the baseline, then you don't need to reset everything in between
  tests.  But if they change, then you need to reset.

* `CiviUnitTestCase`: The older rendition.  Tightly coupled.  Only allows
  one specific baseline.  Mingled with a lot of unrelated functionality in
  `CiviUnitTestCase`.

* `CiviEnvBuilder`: The newer rendition.  Loosely coupled.  Allows different
  baselines.  Can be mixed into any plain-old `TestCase`.  Used by
  `Civi\Test::headless()` and `Civi\Test::e2e()`.

Problem Scenario
----------------

Suppose you have a mix of different tests running with the same DB -- e.g.

1. Run some tests based on `CiviEnvBuilder`
2. Run some tests based on `CiviUnitTestCase`
3. Run some tests based on `CiviEnvBuilder`

This wasn't originally anticipated, but it can happen -- either because
the test-suite is large+mixed, or because a developer is manually
running specific tests (which happen to be written differently).

The problem goes like this:

1. Run some tests based on `CiviEnvBuilder`
    * This resets the DB and also stores a DB signature. ("Here is how
      we setup the DB...")
2. Run some tests based on `CiviUnitTestCase`
    * This resets the DB, but leaves the old DB signature in place.
3. Run some tests based on `CiviEnvBuilder`
    * This sees the old DB signature and wrongly concludes that we
      still have the DB from step (1).

Solution
--------

Whenever one resets the DB, it should update the DB signature.
@civibot
Copy link

civibot bot commented Dec 15, 2022

(Standard links)

@totten
Copy link
Member Author

totten commented Dec 15, 2022

civibot, test this please

1 similar comment
@totten
Copy link
Member Author

totten commented Dec 15, 2022

civibot, test this please

@seamuslee001
Copy link
Contributor

@totten test fails seem to relate

@totten
Copy link
Member Author

totten commented Dec 16, 2022

OK, those test-fails are a good thing -- this means that CiviCRM-Core-PR is now showing the same errors as CiviCRM-Core-Matrix. Yay consistency.

Of course, I guess I need to fix before we merge this. 👀

@@ -54,6 +54,11 @@ public function on_civi_api_prepare(\Civi\API\Event\PrepareEvent $event) {
}
}

public function setUpHeadless() {
// TODO: search_kit should probably be part of the 'headless()' baseline.
return \Civi\Test::headless()->install(['org.civicrm.search_kit'])->apply();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work for getting 5.57 to a passing state. For master, we might move this to the parent (Api4TestBase::setUpHeadless()) or revise the definition of headless().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@totten I'm moving this up to the parent in #25960

@demeritcowboy demeritcowboy merged commit 1f61d73 into civicrm:5.57 Dec 16, 2022
@totten totten deleted the 5.57-testing branch December 16, 2022 03:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants