4 require_once dirname(__DIR__
) . '/src/Mixlib.php';
6 function mixlib(): Mixlib
{
8 if ($mixlib === NULL) {
9 $mixlib = new Mixlib();
15 * Generate a test extension which uses the given mixins..
17 * @param array $options
18 * Ex: ['force' => TRUE]
19 * @param string $targetDir
20 * The output directory.
21 * @param mixed ...$mixinNames
22 * List of mixins to include in the generated extension.
25 function task_create(array $options, string $targetDir, ...$mixinNames) {
26 if (file_exists($targetDir)) {
27 if (!empty($options['force'])) {
28 fprintf(STDOUT
, "Remove %s\n", $targetDir);
29 remove_dir($targetDir);
32 throw new \
RuntimeException("Cannot overwrite $targetDir");
36 $mixinNames = resolve_mixin_names($mixinNames);
37 fprintf(STDOUT
, "Create %s for %s\n", $targetDir, implode(',', $mixinNames));
40 $srcDirs[] = mixer_shimmy_dir();
41 foreach ($mixinNames as $mixinName) {
42 if (is_dir(mixer_mixlib_dir() . "/$mixinName/example")) {
43 $srcDirs[] = mixer_mixlib_dir() . "/$mixinName/example";
46 deep_copy($srcDirs, $targetDir);
49 foreach ($mixinNames as $mixinName) {
50 $mixins[$mixinName] = mixlib()->assertValid(mixlib()->get($mixinName));
53 if (empty($options['bare'])) {
54 mkdir("$targetDir/mixin");
55 file_put_contents("$targetDir/mixin/polyfill.php", mixlib()->get('polyfill')['src']);
56 foreach ($mixins as $mixin) {
57 file_put_contents("$targetDir/mixin/{$mixin['mixinName']}@{$mixin['mixinVersion']}.mixin.php", $mixin['src']);
61 rename(assert_file("$targetDir/info.xml.template"), "$targetDir/info.xml");
62 update_xml("$targetDir/info.xml", function (SimpleXMLElement
$info) use ($mixins) {
63 $mixinsXml = $info->addChild('mixins');
64 foreach ($mixins as $mixinName => $mixin) {
65 $mixinsXml->addChild('mixin', $mixin['mixinName'] . '@' . $mixin['mixinVersion']);
67 if (!empty($options['bare'])) {
68 // If the example doesn't include mixins, then we must get them from elsewhere.
69 $requiresXml = $info->addChild('requires');
70 $requiresXml->addChild('ext', 'mixinlib');
78 * Generate and test an extension that uses the given mixins..
80 * @param array $options
81 * Ex: ['force' => TRUE]
82 * Ex: ['isolate' => TRUE]
83 * @param string $targetDir
84 * The output directory.
85 * @param mixed ...$mixinNames
86 * List of mixins to include in the generated extension.
88 function task_test(array $options, string $targetDir, ...$args) {
89 if (($split = array_search('--', $args)) !== FALSE) {
90 $mixinNames = array_slice($args, 0, $split);
91 $phpunitArgs = array_slice($args, $split +
1);
95 $phpunitArgs = explode(' ', '--group e2e --debug --stop-on-failure');
98 $mixinNames = resolve_mixin_names($mixinNames);
100 if (!empty($options['isolate']) && count($mixinNames) > 1) {
101 foreach ($mixinNames as $mixinName) {
103 task_test($options +
['force' => TRUE], $targetDir, $mixinName, '--', ...$phpunitArgs);
105 catch (\Throwable
$t) {
106 fprintf(STDERR
, "Error testing $mixinName\n%s\n\n", $t->getTraceAsString());
107 $errors = [$mixinName];
111 fprintf(STDERR
, "Error processing mixins: %s\n", implode(' ', $errors));
117 if (is_dir($targetDir) ||
!empty($options['force'])) {
118 $targetDir = task_create($options, $targetDir, ...$mixinNames);
120 if (empty(glob("$targetDir/tests/mixin/*.php"))) {
121 fprintf(STDERR
, "Skip. No tests found for %s\n", implode(',', $mixinNames));
124 fprintf(STDOUT
, "Test %s\n", implode(',', $mixinNames));
125 with_dir($targetDir, function () use ($phpunitArgs) {
126 phpunit($phpunitArgs);
130 function task_list(array $options, ...$mixinNames) {
131 $mixinNames = resolve_mixin_names($mixinNames);
132 foreach ($mixinNames as $mixinName) {
133 $mixin = mixlib()->get($mixinName);
134 fprintf(STDOUT
, "%-20s %-10s %s\n", $mixin['mixinName'], $mixin['mixinVersion'] ??
'', $mixin['description'] ??
'');
138 function task_help(array $options) {
139 $cmd = basename($GLOBALS['argv'][0]);
140 fprintf(STDERR
, "%s - Test utility for extension mixins\n", $cmd);
141 fprintf(STDERR
, "\n");
142 fprintf(STDERR
, "Usage:\n");
143 fprintf(STDERR
, " %s create [-f] [--bare] <new-ext-path> [<mixin-name>...]\n", $cmd);
144 fprintf(STDERR
, " %s test [-f] [--bare] [--isolate] <new-ext-path> [<mixin-name>...] -- [<phpunit-args>...]\n", $cmd);
145 fprintf(STDERR
, " %s list [<mixin-name>...]\n", $cmd);
148 function mixer_mixlib_dir(): string {
149 return dirname(__DIR__
, 3) . '/mixin';
152 function mixer_shimmy_dir(): string {
153 return dirname(__DIR__
, 3) . '/tests/extensions/shimmy';
156 function assert_dir(string $dir): string {
157 if (!file_exists($dir) ||
!is_dir($dir)) {
158 throw new \
RuntimeException("Directory does not exist ($dir)");
163 function assert_file(string $file): string {
164 if (!file_exists($file)) {
165 throw new \
RuntimeException("File does not exist ($file)");
170 function remove_dir(string $dir): void
{
171 if (file_exists($dir)) {
172 passthru_ok("rm -rf " . escapeshellarg($dir));
176 function deep_copy(array $srcDirs, string $targetDir): void
{
177 $srcDirs = (array) $srcDirs;
178 foreach ($srcDirs as $srcDir) {
182 if (!file_exists($targetDir)) {
186 foreach ($srcDirs as $srcDir) {
187 passthru_ok(sprintf('rsync -a %s/./ %s/./', escapeshellarg($srcDir), escapeshellarg($targetDir)));
191 function phpunit(array $args = []) {
192 $argString = implode(' ' , array_map('escapeshellarg', $args));
193 passthru_ok('phpunit8 ' . $argString);
196 function passthru_ok($cmd) {
197 passthru($cmd, $return);
199 throw new \
RuntimeException("Command failed ($cmd)");
203 function resolve_mixin_names(array $mixinNames): array {
204 if (empty($mixinNames)) {
205 return mixlib()->getList();
209 function (string $mixinName) {
210 return trim($mixinName, '/' . DIRECTORY_SEPARATOR
);
215 function with_dir(string $dir, callable
$callback) {
226 function update_xml(string $file, callable
$filter): void
{
227 $dom = new DomDocument();
230 $simpleXml = simplexml_import_dom($dom);
232 // force pretty printing with encode/decode cycle
233 $outXML = $simpleXml->saveXML();
234 $xml = new DOMDocument();
235 $xml->encoding
= 'iso-8859-1';
236 $xml->preserveWhiteSpace
= FALSE;
237 $xml->formatOutput
= TRUE;
238 $xml->loadXML($outXML);
239 file_put_contents($file, $xml->saveXML());
242 function main($args) {
243 $cmd = array_shift($args);
245 $isParsingOptions = TRUE;
246 $newOptions = $newArgs = [];
248 foreach ($args as $arg) {
249 if (!$isParsingOptions) {
252 elseif ($arg === '--') {
253 $isParsingOptions = FALSE;
256 elseif ($arg === '-f') {
257 $newOptions['force'] = TRUE;
259 elseif (preg_match(';^--(bare|isolate|force)$;', $arg, $m)) {
260 $newOptions[$m[1]] = TRUE;
262 elseif ($task === NULL) {
263 $task = 'task_' . preg_replace(';[^\w];', '_', $arg);
270 if (function_exists($task)) {
271 call_user_func($task, $newOptions, ...$newArgs);