Merge pull request #19572 from MegaphoneJon/event-48
[civicrm-core.git] / Civi / Test / CiviEnvBuilder.php
CommitLineData
728bbd5b
TO
1<?php
2namespace Civi\Test;
3
4use Civi\Test\CiviEnvBuilder\CallbackStep;
c0d84418 5use Civi\Test\CiviEnvBuilder\CoreSchemaStep;
ca7abfcc 6use Civi\Test\CiviEnvBuilder\ExtensionsStep;
728bbd5b
TO
7use Civi\Test\CiviEnvBuilder\SqlFileStep;
8use Civi\Test\CiviEnvBuilder\SqlStep;
9use Civi\Test\CiviEnvBuilder\StepInterface;
10use RuntimeException;
11
12/**
13 * Class CiviEnvBuilder
14 *
15 * Provides a fluent interface for tracking a set of steps.
16 * By computing and storing a signature for the list steps, we can
17 * determine whether to (a) do nothing with the list or (b)
18 * reapply all the steps.
19 */
20class CiviEnvBuilder {
21 protected $name;
22
c64f69d9 23 private $steps = [];
728bbd5b
TO
24
25 /**
cc101011 26 * @var string|null
728bbd5b
TO
27 * A digest of the values in $steps.
28 */
29 private $targetSignature = NULL;
30
31 public function __construct($name) {
32 $this->name = $name;
33 }
34
35 public function addStep(StepInterface $step) {
36 $this->targetSignature = NULL;
37 $this->steps[] = $step;
38 return $this;
39 }
40
41 public function callback($callback, $signature = NULL) {
42 return $this->addStep(new CallbackStep($callback, $signature));
43 }
44
c0d84418
TO
45 /**
46 * Generate the core SQL tables.
47 *
48 * @return \Civi\Test\CiviEnvBuilder
49 */
50 public function coreSchema() {
51 return $this->addStep(new CoreSchemaStep());
52 }
53
728bbd5b
TO
54 public function sql($sql) {
55 return $this->addStep(new SqlStep($sql));
56 }
57
58 public function sqlFile($file) {
59 return $this->addStep(new SqlFileStep($file));
60 }
61
62 /**
ca7abfcc 63 * Require that an extension be installed.
728bbd5b 64 *
ca7abfcc
TO
65 * @param string|array $names
66 * One or more extension names. You may use a wildcard '*'.
4b350175 67 * @return CiviEnvBuilder
728bbd5b 68 */
ca7abfcc
TO
69 public function install($names) {
70 return $this->addStep(new ExtensionsStep('install', $names));
728bbd5b
TO
71 }
72
73 /**
ca7abfcc 74 * Require an extension be installed (identified by its directory).
728bbd5b 75 *
ca7abfcc
TO
76 * @param string $dir
77 * The current test directory. We'll search for info.xml to
78 * see what this extension is.
4b350175 79 * @return CiviEnvBuilder
728bbd5b
TO
80 * @throws \CRM_Extension_Exception_ParseException
81 */
ca7abfcc
TO
82 public function installMe($dir) {
83 return $this->addStep(new ExtensionsStep('install', $this->whoAmI($dir)));
84 }
85
86 /**
87 * Require an extension be uninstalled.
88 *
89 * @param string|array $names
90 * One or more extension names. You may use a wildcard '*'.
4b350175 91 * @return CiviEnvBuilder
ca7abfcc
TO
92 */
93 public function uninstall($names) {
94 return $this->addStep(new ExtensionsStep('uninstall', $names));
95 }
96
97 /**
98 * Require an extension be uninstalled (identified by its directory).
99 *
100 * @param string $dir
101 * The current test directory. We'll search for info.xml to
102 * see what this extension is.
4b350175 103 * @return CiviEnvBuilder
ca7abfcc
TO
104 * @throws \CRM_Extension_Exception_ParseException
105 */
106 public function uninstallMe($dir) {
107 return $this->addStep(new ExtensionsStep('uninstall', $this->whoAmI($dir)));
728bbd5b
TO
108 }
109
110 protected function assertValid() {
111 foreach ($this->steps as $step) {
112 if (!$step->isValid()) {
113 throw new RuntimeException("Found invalid step: " . var_dump($step, 1));
114 }
115 }
116 }
117
118 /**
119 * @return string
120 */
121 protected function getTargetSignature() {
122 if ($this->targetSignature === NULL) {
123 $buf = '';
124 foreach ($this->steps as $step) {
125 $buf .= $step->getSig();
126 }
127 $this->targetSignature = md5($buf);
128 }
129
130 return $this->targetSignature;
131 }
132
133 /**
134 * @return string
135 */
136 protected function getSavedSignature() {
137 $liveSchemaRev = NULL;
138 $pdo = \Civi\Test::pdo();
139 $pdoStmt = $pdo->query(sprintf(
140 "SELECT rev FROM %s.civitest_revs WHERE name = %s",
141 \Civi\Test::dsn('database'),
142 $pdo->quote($this->name)
143 ));
144 foreach ($pdoStmt as $row) {
145 $liveSchemaRev = $row['rev'];
146 }
147 return $liveSchemaRev;
148 }
149
150 /**
151 * @param $newSignature
152 */
153 protected function setSavedSignature($newSignature) {
154 $pdo = \Civi\Test::pdo();
155 $query = sprintf(
156 'INSERT INTO %s.civitest_revs (name,rev) VALUES (%s,%s) '
157 . 'ON DUPLICATE KEY UPDATE rev = %s;',
158 \Civi\Test::dsn('database'),
159 $pdo->quote($this->name),
160 $pdo->quote($newSignature),
161 $pdo->quote($newSignature)
162 );
163
164 if (\Civi\Test::execute($query) === FALSE) {
165 throw new RuntimeException("Failed to flag schema version: $query");
166 }
167 }
168
169 /**
ca7abfcc
TO
170 * Determine if there's been a change in the preferred configuration.
171 * If the preferred-configuration matches the last test, keep it. Otherwise,
172 * destroy and recreate.
728bbd5b
TO
173 *
174 * @param bool $force
ca7abfcc
TO
175 * Forcibly execute the build, even if the configuration hasn't changed.
176 * This will slow-down the tests, but it may be appropriate for some very sloppy
177 * tests.
4b350175 178 * @return CiviEnvBuilder
728bbd5b
TO
179 */
180 public function apply($force = FALSE) {
9d114ab6
TO
181 return \Civi\Test::asPreInstall(function() use ($force) {
182 $dbName = \Civi\Test::dsn('database');
183 $query = "USE {$dbName};"
184 . "CREATE TABLE IF NOT EXISTS civitest_revs (name VARCHAR(64) PRIMARY KEY, rev VARCHAR(64));";
728bbd5b 185
9d114ab6
TO
186 if (\Civi\Test::execute($query) === FALSE) {
187 throw new \RuntimeException("Failed to flag schema version: $query");
188 }
728bbd5b 189
9d114ab6 190 $this->assertValid();
728bbd5b 191
9d114ab6
TO
192 if (!$force && $this->getSavedSignature() === $this->getTargetSignature()) {
193 return $this;
194 }
195 foreach ($this->steps as $step) {
196 $step->run($this);
197 }
198 $this->setSavedSignature($this->getTargetSignature());
728bbd5b 199 return $this;
9d114ab6 200 });
728bbd5b
TO
201 }
202
ca7abfcc
TO
203 /**
204 * @param $dir
205 * @return null
206 * @throws \CRM_Extension_Exception_ParseException
207 */
208 protected function whoAmI($dir) {
209 while ($dir && dirname($dir) !== $dir && !file_exists("$dir/info.xml")) {
210 $dir = dirname($dir);
211 }
212 if (file_exists("$dir/info.xml")) {
213 $info = \CRM_Extension_Info::loadFromFile("$dir/info.xml");
214 $name = $info->key;
215 return $name;
216 }
217 return $name;
218 }
219
728bbd5b 220}