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