10 * A facade for managing the test environment.
17 private static $singletons = [];
22 public static $statics = [];
25 * Run code in a pre-boot fashion.
27 * @param callable $callback
29 * Pass through the result of the callback.
31 public static function asPreInstall($callback) {
32 $conn = \Civi\Test
::pdo();
34 $oldEscaper = \CRM_Core_I18n
::$SQL_ESCAPER;
35 \Civi
::$statics['testPreInstall'] = (\Civi
::$statics['testPreInstall'] ??
0) +
1;
37 \CRM_Core_I18n
::$SQL_ESCAPER = function ($text) use ($conn) {
38 return substr($conn->quote($text), 1, -1);
42 \CRM_Core_I18n
::$SQL_ESCAPER = $oldEscaper;
43 \Civi
::$statics['testPreInstall']--;
44 if (\Civi
::$statics['testPreInstall'] <= 0) {
45 unset(\Civi
::$statics['testPreInstall']);
51 * Get the data source used for testing.
53 * @param string|NULL $part
54 * One of NULL, 'hostspec', 'port', 'username', 'password', 'database'.
55 * @return string|array|NULL
56 * If $part is omitted, return full DSN array.
57 * If $part is a string, return that part of the DSN.
59 public static function dsn($part = NULL) {
60 if (!isset(self
::$singletons['dsn'])) {
61 require_once "DB.php";
62 $dsn = \CRM_Utils_SQL
::autoSwitchDSN(CIVICRM_DSN
);
63 self
::$singletons['dsn'] = \DB
::parseDSN($dsn);
67 return self
::$singletons['dsn'];
70 if (isset(self
::$singletons['dsn'][$part])) {
71 return self
::$singletons['dsn'][$part];
78 * Get a connection to the test database.
82 public static function pdo() {
83 if (!isset(self
::$singletons['pdo'])) {
84 $dsninfo = self
::dsn();
85 $host = $dsninfo['hostspec'];
86 $port = @$dsninfo['port'];
88 self
::$singletons['pdo'] = new PDO("mysql:host={$host}" . ($port ?
";port=$port" : ""),
89 $dsninfo['username'], $dsninfo['password'],
90 [PDO
::MYSQL_ATTR_USE_BUFFERED_QUERY
=> TRUE]
93 catch (PDOException
$e) {
94 echo "Can't connect to MySQL server:" . PHP_EOL
. $e->getMessage() . PHP_EOL
;
98 return self
::$singletons['pdo'];
102 * Create a builder for the headless environment.
105 * \Civi\Test::headless()->apply();
106 * \Civi\Test::headless()->sqlFile('ex.sql')->apply();
109 * @return \Civi\Test\CiviEnvBuilder
111 public static function headless() {
112 $civiRoot = dirname(__DIR__
);
113 $builder = new \Civi\Test\
CiviEnvBuilder('CiviEnvBuilder');
115 ->callback(function ($ctx) {
116 if (CIVICRM_UF
!== 'UnitTests') {
117 throw new \
RuntimeException("\\Civi\\Test::headless() requires CIVICRM_UF=UnitTests");
119 $dbName = \Civi\Test
::dsn('database');
120 echo "Installing {$dbName} schema\n";
121 \Civi\Test
::schema()->dropAll();
124 ->sql("DELETE FROM civicrm_extension")
125 ->callback(function ($ctx) {
126 \Civi\Test
::data()->populate();
132 * Create a builder for end-to-end testing on the live environment.
135 * \Civi\Test::e2e()->apply();
136 * \Civi\Test::e2e()->install('foo.bar')->apply();
139 * @return \Civi\Test\CiviEnvBuilder
141 public static function e2e() {
142 $builder = new \Civi\Test\
CiviEnvBuilder('CiviEnvBuilder');
144 ->callback(function ($ctx) {
145 if (CIVICRM_UF
=== 'UnitTests') {
146 throw new \
RuntimeException("\\Civi\\Test::e2e() requires a real CMS. Found CIVICRM_UF=UnitTests.");
153 * @return \Civi\Test\Schema
155 public static function schema() {
156 if (!isset(self
::$singletons['schema'])) {
157 self
::$singletons['schema'] = new \Civi\Test\
Schema();
159 return self
::$singletons['schema'];
163 * @return \CRM_Core_CodeGen_Main
165 public static function codeGen() {
166 if (!isset(self
::$singletons['codeGen'])) {
167 $civiRoot = str_replace(DIRECTORY_SEPARATOR
, '/', dirname(__DIR__
));
168 $codeGen = new \
CRM_Core_CodeGen_Main("$civiRoot/CRM/Core/DAO", "$civiRoot/sql", $civiRoot, "$civiRoot/templates", NULL, "UnitTests", NULL, "$civiRoot/xml/schema/Schema.xml", NULL);
170 self
::$singletons['codeGen'] = $codeGen;
172 return self
::$singletons['codeGen'];
176 * @return \Civi\Test\Data
178 public static function data() {
179 if (!isset(self
::$singletons['data'])) {
180 self
::$singletons['data'] = new \Civi\Test\
Data('CiviTesterData');
182 return self
::$singletons['data'];
186 * @return \Civi\Test\ExampleDataLoader
188 public static function examples(): \Civi\Test\ExampleDataLoader
{
189 if (!isset(self
::$singletons['examples'])) {
190 self
::$singletons['examples'] = new \Civi\Test\
ExampleDataLoader();
192 return self
::$singletons['examples'];
196 * Lookup the content of an example data-set.
198 * This helper is for the common case of looking up the data for a specific example.
199 * If you need more detailed information (eg the list of examples or other metadata),
200 * then use `\Civi\Test::examples(): ExampleDataLoader`. It provides more methods.
202 * @param string $name
203 * Symbolic name of the data-set.
207 public static function example(string $name): array {
208 $result = static::examples()->getFull($name);
209 if (!isset($result['data'])) {
210 throw new \
CRM_Core_Exception("Failed to load example data-set: $name");
212 return $result['data'];
216 * @return \Civi\Test\EventChecker
218 public static function eventChecker() {
219 if (!isset(self
::$singletons['eventChecker'])) {
220 self
::$singletons['eventChecker'] = new \Civi\Test\
EventChecker();
222 return self
::$singletons['eventChecker'];
226 * Prepare and execute a batch of SQL statements.
228 * @param string $query
231 public static function execute($query) {
232 $pdo = \Civi\Test
::pdo();
234 $string = preg_replace("/^#[^\n]*$/m", "\n", $query);
235 $string = preg_replace("/^(--[^-]).*/m", "\n", $string);
237 $queries = preg_split('/;\s*$/m', $string);
238 foreach ($queries as $query) {
239 $query = trim($query);
240 if (!empty($query)) {
241 $result = $pdo->query($query);
242 if ($pdo->errorCode() == 0) {
247 var_dump($pdo->errorInfo());
248 // die( "Cannot execute $query: " . $pdo->errorInfo() );