Browse Source

Merge branch '1.4'

Jordi Boggiano 8 years ago
parent
commit
3c1e7a67cf

+ 2 - 2
src/Composer/IO/ConsoleIO.php

@@ -16,7 +16,7 @@ use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\ConsoleOutputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Helper\HelperSet;
-use Symfony\Component\Console\Question\ConfirmationQuestion;
+use Composer\Question\StrictConfirmationQuestion;
 use Symfony\Component\Console\Question\Question;
 
 /**
@@ -247,7 +247,7 @@ class ConsoleIO extends BaseIO
     {
         /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
         $helper = $this->helperSet->get('question');
-        $question = new ConfirmationQuestion($question, $default);
+        $question = new StrictConfirmationQuestion($question, $default);
 
         return $helper->ask($this->input, $this->getErrorOutput(), $question);
     }

+ 94 - 0
src/Composer/Question/StrictConfirmationQuestion.php

@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Question;
+
+use Symfony\Component\Console\Exception\InvalidArgumentException;
+use Symfony\Component\Console\Question\Question;
+
+/**
+ * Represents a yes/no question
+ * Enforces strict responses rather than non-standard answers counting as default
+ * Based on Symfony\Component\Console\Question\ConfirmationQuestion
+ *
+ * @author Theo Tonge <theo@theotonge.co.uk>
+ */
+class StrictConfirmationQuestion extends Question
+{
+    private $trueAnswerRegex;
+    private $falseAnswerRegex;
+
+    /**
+     * Constructor.s
+     *
+     * @param string $question         The question to ask to the user
+     * @param bool   $default          The default answer to return, true or false
+     * @param string $trueAnswerRegex  A regex to match the "yes" answer
+     * @param string $falseAnswerRegex A regex to match the "no" answer
+     */
+    public function __construct($question, $default = true, $trueAnswerRegex = '/^y(?:es)?$/i', $falseAnswerRegex = '/^no?$/i')
+    {
+        parent::__construct($question, (bool) $default);
+
+        $this->trueAnswerRegex = $trueAnswerRegex;
+        $this->falseAnswerRegex = $falseAnswerRegex;
+        $this->setNormalizer($this->getDefaultNormalizer());
+        $this->setValidator($this->getDefaultValidator());
+    }
+
+    /**
+     * Returns the default answer normalizer.
+     *
+     * @return callable
+     */
+    private function getDefaultNormalizer()
+    {
+        $default = $this->getDefault();
+        $trueRegex = $this->trueAnswerRegex;
+        $falseRegex = $this->falseAnswerRegex;
+
+        return function ($answer) use ($default, $trueRegex, $falseRegex) {
+            if (is_bool($answer)) {
+                return $answer;
+            }
+            if (empty($answer) && !empty($default)) {
+                return $default;
+            }
+
+            if (preg_match($trueRegex, $answer)) {
+                return true;
+            }
+
+            if (preg_match($falseRegex, $answer)) {
+                return false;
+            }
+
+            return null;
+        };
+    }
+
+    /**
+     * Returns the default answer validator.
+     *
+     * @return callable
+     */
+    private function getDefaultValidator()
+    {
+        return function ($answer) {
+            if (!is_bool($answer)) {
+                throw new InvalidArgumentException('Please answer yes, y, no, or n.');
+            }
+
+            return $answer;
+        };
+    }
+}

+ 1 - 1
tests/Composer/Test/IO/ConsoleIOTest.php

@@ -179,7 +179,7 @@ class ConsoleIOTest extends TestCase
             ->with(
                 $this->isInstanceOf('Symfony\Component\Console\Input\InputInterface'),
                 $this->isInstanceOf('Symfony\Component\Console\Output\OutputInterface'),
-                $this->isInstanceOf('Symfony\Component\Console\Question\ConfirmationQuestion')
+                $this->isInstanceOf('Composer\Question\StrictConfirmationQuestion')
             )
         ;
 

+ 110 - 0
tests/Composer/Test/Question/StrictConfirmationQuestionTest.php

@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Question\Test;
+
+use Composer\Question\StrictConfirmationQuestion;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Console\Exception\InvalidArgumentException;
+use Symfony\Component\Console\Helper\QuestionHelper;
+use Symfony\Component\Console\Output\StreamOutput;
+
+/**
+ * based on Symfony\Component\Console\Tests\Helper\QuestionHelperTest
+ *
+ * @author Theo Tonge <theo@theotonge.co.uk>
+ */
+class StrictConfirmationQuestionTest extends TestCase
+{
+    public function getAskConfirmationBadData()
+    {
+        return array(
+            array('not correct'),
+            array('no more'),
+            array('yes please'),
+            array('yellow'),
+        );
+    }
+
+    /**
+     * @expectedException InvalidArgumentException
+     * @expectedExceptionMessage Please answer yes, y, no, or n.
+     * @dataProvider             getAskConfirmationBadData
+     */
+    public function testAskConfirmationBadAnswer($answer)
+    {
+        $dialog = new QuestionHelper();
+        $dialog->setInputStream($this->getInputStream($answer."\n"));
+        $question = new StrictConfirmationQuestion('Do you like French fries?');
+        $question->setMaxAttempts(1);
+        $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
+    }
+
+    /**
+     * @dataProvider getAskConfirmationData
+     */
+    public function testAskConfirmation($question, $expected, $default = true)
+    {
+        $dialog = new QuestionHelper();
+
+        $dialog->setInputStream($this->getInputStream($question."\n"));
+        $question = new StrictConfirmationQuestion('Do you like French fries?', $default);
+        $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel'));
+    }
+
+    public function getAskConfirmationData()
+    {
+        return array(
+            array('', true),
+            array('', false, false),
+            array('y', true),
+            array('yes', true),
+            array('n', false),
+            array('no', false),
+        );
+    }
+
+    public function testAskConfirmationWithCustomTrueAndFalseAnswer()
+    {
+        $dialog = new QuestionHelper();
+
+        $question = new StrictConfirmationQuestion('Do you like French fries?', false, '/^ja$/i', '/^nein$/i');
+        $dialog->setInputStream($this->getInputStream("ja\n"));
+        $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
+        $dialog->setInputStream($this->getInputStream("nein\n"));
+        $this->assertFalse($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
+    }
+
+    protected function getInputStream($input)
+    {
+        $stream = fopen('php://memory', 'r+', false);
+        fwrite($stream, $input);
+        rewind($stream);
+
+        return $stream;
+    }
+
+    protected function createOutputInterface()
+    {
+        return new StreamOutput(fopen('php://memory', 'r+', false));
+    }
+
+    protected function createInputInterfaceMock($interactive = true)
+    {
+        $mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
+        $mock->expects($this->any())
+            ->method('isInteractive')
+            ->will($this->returnValue($interactive));
+
+        return $mock;
+    }
+}

+ 1 - 1
tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php

@@ -123,7 +123,7 @@ class GitBitbucketDriverTest extends TestCase
                 ),
                 array(
                     $this->originUrl,
-                    'https://api.bitbucket.org/2.0/repositories/user/repo/refs/branches?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cnext&sort=-target.date',
+                    'https://api.bitbucket.org/2.0/repositories/user/repo/refs/branches?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cvalues.heads%2Cnext&sort=-target.date',
                     false,
                 ),
                 array(