Merge pull request #17813 from JKingsnorth/patch-15
[civicrm-core.git] / Civi / Test.php
1 <?php
2 namespace Civi;
3
4 use PDO;
5 use PDOException;
6
7 /**
8 * Class Test
9 *
10 * A facade for managing the test environment.
11 */
12 class Test {
13
14 /**
15 * @var array
16 */
17 private static $singletons = [];
18
19 /**
20 * @var array
21 */
22 public static $statics = [];
23
24 /**
25 * Run code in a pre-boot fashion.
26 *
27 * @param callable $callback
28 * @return mixed
29 * Pass through the result of the callback.
30 */
31 public static function asPreInstall($callback) {
32 $conn = \Civi\Test::pdo();
33
34 $oldEscaper = \CRM_Core_I18n::$SQL_ESCAPER;
35 \Civi::$statics['testPreInstall'] = (\Civi::$statics['testPreInstall'] ?? 0) + 1;
36 try {
37 \CRM_Core_I18n::$SQL_ESCAPER = function ($text) use ($conn) {
38 return substr($conn->quote($text), 1, -1);
39 };
40 return $callback();
41 } finally {
42 \CRM_Core_I18n::$SQL_ESCAPER = $oldEscaper;
43 \Civi::$statics['testPreInstall']--;
44 if (\Civi::$statics['testPreInstall'] <= 0) {
45 unset(\Civi::$statics['testPreInstall']);
46 }
47 }
48 }
49
50 /**
51 * Get the data source used for testing.
52 *
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.
58 */
59 public static function dsn($part = NULL) {
60 if (!isset(self::$singletons['dsn'])) {
61 require_once "DB.php";
62 self::$singletons['dsn'] = \DB::parseDSN(CIVICRM_DSN);
63 }
64
65 if ($part === NULL) {
66 return self::$singletons['dsn'];
67 }
68
69 if (isset(self::$singletons['dsn'][$part])) {
70 return self::$singletons['dsn'][$part];
71 }
72
73 return NULL;
74 }
75
76 /**
77 * Get a connection to the test database.
78 *
79 * @return \PDO
80 */
81 public static function pdo() {
82 if (!isset(self::$singletons['pdo'])) {
83 $dsninfo = self::dsn();
84 $host = $dsninfo['hostspec'];
85 $port = @$dsninfo['port'];
86 try {
87 self::$singletons['pdo'] = new PDO("mysql:host={$host}" . ($port ? ";port=$port" : ""),
88 $dsninfo['username'], $dsninfo['password'],
89 [PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE]
90 );
91 }
92 catch (PDOException $e) {
93 echo "Can't connect to MySQL server:" . PHP_EOL . $e->getMessage() . PHP_EOL;
94 exit(1);
95 }
96 }
97 return self::$singletons['pdo'];
98 }
99
100 /**
101 * Create a builder for the headless environment.
102 *
103 * ```
104 * \Civi\Test::headless()->apply();
105 * \Civi\Test::headless()->sqlFile('ex.sql')->apply();
106 * ```
107 *
108 * @return \Civi\Test\CiviEnvBuilder
109 */
110 public static function headless() {
111 $civiRoot = dirname(__DIR__);
112 $builder = new \Civi\Test\CiviEnvBuilder('CiviEnvBuilder');
113 $builder
114 ->callback(function ($ctx) {
115 if (CIVICRM_UF !== 'UnitTests') {
116 throw new \RuntimeException("\\Civi\\Test::headless() requires CIVICRM_UF=UnitTests");
117 }
118 $dbName = \Civi\Test::dsn('database');
119 echo "Installing {$dbName} schema\n";
120 \Civi\Test::schema()->dropAll();
121 }, 'headless-drop')
122 ->coreSchema()
123 ->sql("DELETE FROM civicrm_extension")
124 ->callback(function ($ctx) {
125 \Civi\Test::data()->populate();
126 }, 'populate');
127 return $builder;
128 }
129
130 /**
131 * Create a builder for end-to-end testing on the live environment.
132 *
133 * ```
134 * \Civi\Test::e2e()->apply();
135 * \Civi\Test::e2e()->install('foo.bar')->apply();
136 * ```
137 *
138 * @return \Civi\Test\CiviEnvBuilder
139 */
140 public static function e2e() {
141 $builder = new \Civi\Test\CiviEnvBuilder('CiviEnvBuilder');
142 $builder
143 ->callback(function ($ctx) {
144 if (CIVICRM_UF === 'UnitTests') {
145 throw new \RuntimeException("\\Civi\\Test::e2e() requires a real CMS. Found CIVICRM_UF=UnitTests.");
146 }
147 }, 'e2e-check');
148 return $builder;
149 }
150
151 /**
152 * @return \Civi\Test\Schema
153 */
154 public static function schema() {
155 if (!isset(self::$singletons['schema'])) {
156 self::$singletons['schema'] = new \Civi\Test\Schema();
157 }
158 return self::$singletons['schema'];
159 }
160
161 /**
162 * @return \CRM_Core_CodeGen_Main
163 */
164 public static function codeGen() {
165 if (!isset(self::$singletons['codeGen'])) {
166 $civiRoot = str_replace(DIRECTORY_SEPARATOR, '/', dirname(__DIR__));
167 $codeGen = new \CRM_Core_CodeGen_Main("$civiRoot/CRM/Core/DAO", "$civiRoot/sql", $civiRoot, "$civiRoot/templates", NULL, "UnitTests", NULL, "$civiRoot/xml/schema/Schema.xml", NULL);
168 $codeGen->init();
169 self::$singletons['codeGen'] = $codeGen;
170 }
171 return self::$singletons['codeGen'];
172 }
173
174 /**
175 * @return \Civi\Test\Data
176 */
177 public static function data() {
178 if (!isset(self::$singletons['data'])) {
179 self::$singletons['data'] = new \Civi\Test\Data('CiviTesterData');
180 }
181 return self::$singletons['data'];
182 }
183
184 /**
185 * Prepare and execute a batch of SQL statements.
186 *
187 * @param string $query
188 * @return bool
189 */
190 public static function execute($query) {
191 $pdo = \Civi\Test::pdo();
192
193 $string = preg_replace("/^#[^\n]*$/m", "\n", $query);
194 $string = preg_replace("/^(--[^-]).*/m", "\n", $string);
195
196 $queries = preg_split('/;\s*$/m', $string);
197 foreach ($queries as $query) {
198 $query = trim($query);
199 if (!empty($query)) {
200 $result = $pdo->query($query);
201 if ($pdo->errorCode() == 0) {
202 continue;
203 }
204 else {
205 var_dump($result);
206 var_dump($pdo->errorInfo());
207 // die( "Cannot execute $query: " . $pdo->errorInfo() );
208 }
209 }
210 }
211 return TRUE;
212 }
213
214 }