Commit | Line | Data |
---|---|---|
728bbd5b TO |
1 | <?php |
2 | namespace Civi\Test; | |
3 | ||
4 | use Civi\Test\CiviEnvBuilder\CallbackStep; | |
c0d84418 | 5 | use Civi\Test\CiviEnvBuilder\CoreSchemaStep; |
ca7abfcc | 6 | use Civi\Test\CiviEnvBuilder\ExtensionsStep; |
728bbd5b TO |
7 | use Civi\Test\CiviEnvBuilder\SqlFileStep; |
8 | use Civi\Test\CiviEnvBuilder\SqlStep; | |
9 | use Civi\Test\CiviEnvBuilder\StepInterface; | |
10 | use 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 | */ | |
20 | class 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 | } |