--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Test;
+
+use PHPUnit\Framework\Assert;
+
+/**
+ * An EventCheck is a fragment of a unit-test -- it is mixed into
+ * various test-scenarios and applies extra assertions.
+ */
+class EventCheck extends Assert {
+
+ /**
+ * @var \PHPUnit\Framework\Test
+ */
+ private $test;
+
+ /**
+ * Determine whether this check should be used during the current test.
+ *
+ * @param \PHPUnit\Framework\Test|NULL $test
+ *
+ * @return bool|string
+ * FALSE: The check will be completely skipped.
+ * TRUE: The check will be enabled. However, if the events never
+ * execute, that is OK. Useful for general compliance-testing.
+ */
+ public function isSupported($test) {
+ return TRUE;
+ }
+
+ /**
+ * @return \PHPUnit\Framework\Test|NULL
+ */
+ public function getTest() {
+ return $this->test;
+ }
+
+ /**
+ * @param \PHPUnit\Framework\Test|NULL $test
+ */
+ public function setTest($test): void {
+ $this->test = $test;
+ }
+
+ /**
+ * Assert that a variable has a given type.
+ *
+ * @param string|string[] $types
+ * List of types, per `gettype()` or `get_class()`
+ * Ex: 'int|string|NULL'
+ * Ex: [`array`, `NULL`, `CRM_Core_DAO`]
+ * @param mixed $value
+ * The variable to check
+ * @param string|NULL $msg
+ * @see \CRM_Utils_Type::validatePhpType
+ */
+ public function assertType($types, $value, ?string $msg = NULL) {
+ if (!\CRM_Utils_Type::validatePhpType($value, $types, FALSE)) {
+ $defactoType = is_object($value) ? get_class($value) : gettype($value);
+ $types = is_array($types) ? implode('|', $types) : $types;
+ $this->fail(sprintf("Expected one of (%s) but found %s\n%s", $types, $defactoType, $msg));
+ }
+ }
+
+ public function setUp() {
+ }
+
+ public function tearDown() {
+ }
+
+}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved. |
+ | |
+ | This work is published under the GNU AGPLv3 license with some |
+ | permitted exceptions and without any warranty. For full license |
+ | and copyright information, see https://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Test;
+
+use Civi\Core\Event\EventScanner;
+
+class EventChecker {
+
+ /**
+ * @var \Civi\Test\EventCheck[]|null
+ */
+ private $allChecks = NULL;
+
+ /**
+ * @var \Civi\Test\EventCheck[]|null
+ */
+ private $activeChecks = NULL;
+
+ /**
+ * @param \PHPUnit\Framework\Test $test
+ *
+ * @return $this
+ */
+ public function start(\PHPUnit\Framework\Test $test) {
+ if ($this->activeChecks === NULL) {
+ $this->activeChecks = [];
+ foreach ($this->findAll() as $template) {
+ /** @var EventCheck $template */
+ if ($template->isSupported($test)) {
+ $checker = clone $template;
+ $checker->setTest($test);
+ $this->activeChecks[] = $checker;
+ $checker->setUp();
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function addListeners() {
+ $d = \Civi::dispatcher();
+ foreach ($this->activeChecks ?: [] as $checker) {
+ /** @var EventCheck $checker */
+ $d->addListenerMap($checker, EventScanner::findListeners($checker));
+ // For the moment, KISS. But we may want a counter at some point - to ensure things actually run.
+ //foreach (EventScanner::findListeners($checker) as $event => $listeners) {
+ // foreach ($listeners as $listener) {
+ // $d->addListener($event,
+ // function($args...) use ($listener) {
+ // $count++;
+ // $m = $listener[1];
+ // $checker->$m(...$args);
+ // },
+ // $listener[1] ?? 0
+ // );
+ // }
+ //}
+ }
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function stop() {
+ // NOTE: In test environment, dispatcher will be removed regardless.
+ foreach ($this->activeChecks ?? [] as $checker) {
+ /** @var \Civi\Test\EventCheck $checker */
+ Invasive::call([$checker, 'tearDown']);
+ $checker->setTest(NULL);
+ }
+ $this->activeChecks = NULL;
+ return $this;
+ }
+
+ /**
+ * @return EventCheck[]
+ */
+ protected function findAll() {
+ if ($this->allChecks === NULL) {
+ $all = [];
+ $testDir = \Civi::paths()->getPath('[civicrm.root]/tests/events');
+ $files = \CRM_Utils_File::findFiles($testDir, '*.evch.php', TRUE);
+ sort($files);
+ foreach ($files as $file) {
+ $all[$file] = require $testDir . '/' . $file;
+ }
+ $this->allChecks = $all;
+ }
+
+ return $this->allChecks;
+ }
+
+}