diff --git a/.phpspec/class.tpl b/.phpspec/class.tpl new file mode 100644 index 0000000..af55bd5 --- /dev/null +++ b/.phpspec/class.tpl @@ -0,0 +1,16 @@ + + * @copyright 2013-2016 Hexmedia.pl + * @license @see LICENSE + */%namespace_block% + +/** + * Class Application + * + * @package %namespace% + */ +class %name% +{ +} diff --git a/.sensiolabs.yml b/.sensiolabs.yml index 5a23935..dd1cf0b 100644 --- a/.sensiolabs.yml +++ b/.sensiolabs.yml @@ -1,6 +1,7 @@ global_exclude_dirs: - vendor - Tests + - src-dev rules: git.user_specific_ignored_file: diff --git a/.travis.yml b/.travis.yml index 5a04b47..8d91ead 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ matrix: allow_failures: - env: DEPS='dev' - php: nightly - - php: 7.0 before_install: - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then cat $HOME/.phpenv/versions/$TRAVIS_PHP_VERSION/etc/conf.d/xdebug.ini > ./xdebug.ini ; fi @@ -45,6 +44,7 @@ before_install: - echo '#!/bin/bash' > install.sh - echo -n "composer update" >> install.sh - echo -n " --prefer-dist" >> install.sh + - 'if [ "$TRAVIS_PHP_VERSION" == "7.0" ] || [ "$TRAVIS_PHP_VERSION" == "nightly" ]; then cat composer.json | sed -e "$(cat composer.json | grep -n "phpspec/phpspec\": \"~2.4\"" | cut -f1 -d:)s/.*/\"phpspec\/phpspec\": \"~2.5@dev\",/" > composer.json; fi' - if [ "$DEPS" == "low" ]; then echo -n " --prefer-lowest" >> install.sh; fi; - if [ "$DEPS" == "normal" ]; then echo -n " --prefer-stable" >> install.sh; fi; - sed -n '/prefer-stable/!p' composer.json > tmp.json && mv tmp.json composer.json; diff --git a/TODO.md b/TODO.md index 95f6b21..f40ee44 100644 --- a/TODO.md +++ b/TODO.md @@ -1,19 +1,24 @@ +Tests: +------ +* Configure at least one osx(travis) environment to test if it is working on osx. + This version ------------ * System reader should also use factory. -* Read should be done always via System/*.php (eg. for Unix System/Unix.php) -* Configure at least one osx(travis) environment to test if it is working on osx. +* Readers needs to be checked and refactored * Check names of all systems that should be supported. All Unix like. -* Add phpspec to existing commands +* Add specs to existing commands * Try integration with Symfony CrontabBundle. This will need some commands to be rewritten -* Write behat tests * Unify exception names. +* Change beforeComment to comment +* Add protection for special strings in unix, if they exists, this should not work. Next Versions ------------- -* support for windows Tasks (more info: http://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron, https://msdn.microsoft.com/en-us/library/windows/desktop/bb736357(v=vs.85).aspx) -* support for special strings in unix cron (only reading will be enough i think), and possibility to setup cron for startup. -* create comment object that can be added to task or variable +------------- +* support for special strings in unix cron, and possibility to setup cron for startup. * support for comments in variables, currently, all comments between variables will be ignored, and removed. -* Why appveyor is not resulting in error after runing travis * add support for importing tasks from crontab to file, should be interactive command. + +In future +--------- +* support for windows Tasks (more info: http://stackoverflow.com/questions/132971/what-is-the-windows-version-of-cron, https://msdn.microsoft.com/en-us/library/windows/desktop/bb736357(v=vs.85).aspx) diff --git a/Tests/example_configurations/empty.xml b/Tests/example_configurations/empty.xml new file mode 100755 index 0000000..4ce5b31 --- /dev/null +++ b/Tests/example_configurations/empty.xml @@ -0,0 +1,3 @@ + + + diff --git a/Tests/example_configurations/test.ini b/Tests/example_configurations/test.ini index 0d09392..621d285 100755 --- a/Tests/example_configurations/test.ini +++ b/Tests/example_configurations/test.ini @@ -22,3 +22,11 @@ machine = www103 variables.MAILTO = test@test.com variables.NO_VALIDATE = true +[command_without_log_file] +command = ./some/command_without_log_file +minute = * +hour = * +day_of_month = * +day_of_week = * +month = * +machine = www103 diff --git a/Tests/example_configurations/test.json b/Tests/example_configurations/test.json index 1086705..f86220d 100755 --- a/Tests/example_configurations/test.json +++ b/Tests/example_configurations/test.json @@ -1,29 +1,40 @@ { - "s3_synchronize": { - "command": "mwe:photo:s3 asgasg afasf", + "command1": { + "command": "./some/command1", "minute": "*/13", "hour": "*", "day_of_month": "*", "day_of_week": "*", "month": "*", - "log_file": "./logs/schema_validate.log", + "log_file": "./logs/some_log1.log", "machine": "www*", + "comment": "this is some comment for this crontab", "variables": { - "MAILTO": "k.kuczek@tvn.pl" + "MAILTO": "test@test.pl" } }, - "s3_synchronize2": { - "command": "mwe:photo:s3 asgasg afasf", + "command2": { + "command": "./some/command2", "minute": "*", "hour": "*", "day_of_month": "*", "day_of_week": "*", "month": "*", - "log_file": "./logs/schema_validate.log", + "log_file": "./logs/some_log2.log", "machine": "www103", "variables": { - "MAILTO": "j.zieba@tvn.pl" + "MAILTO": "test@test.com", + "NO_VALIDATE": true } + }, + "command_without_log_file": { + "command": "./some/command_without_log_file", + "minute": "*", + "hour": "*", + "day_of_month": "*", + "day_of_week": "*", + "month": "*", + "machine": "www103" } } diff --git a/Tests/example_configurations/test.xml b/Tests/example_configurations/test.xml index 5705baa..b7f3516 100755 --- a/Tests/example_configurations/test.xml +++ b/Tests/example_configurations/test.xml @@ -1,30 +1,43 @@ + - s3_synchornize - mwe:photo:s3 asgasg afasf + command1 + ./some/command1 */13 * * * * - ./logs/schema_validate.log + ./logs/some_log1.log www* + this is some comment for this crontab - k.kuczek@tvn.pl + test@test.pl - s3_synchornize2 - mwe:photo:s3 asgasg afasf + command2 + ./some/command2 * * * * * - ./logs/schema_validate.log + ./logs/some_log2.log www103 - j.zieba@tvn.pl + test@test.com + 1 + + command_without_log_file + ./some/command_without_log_file + * + * + * + * + * + www103 + diff --git a/Tests/example_configurations/test.yaml b/Tests/example_configurations/test.yml similarity index 71% rename from Tests/example_configurations/test.yaml rename to Tests/example_configurations/test.yml index 03a4283..8d3edfe 100755 --- a/Tests/example_configurations/test.yaml +++ b/Tests/example_configurations/test.yml @@ -22,4 +22,12 @@ command2: variables: MAILTO: test@test.com NO_VALIDATE: true +command_without_log_file: + command: "./some/command_without_log_file" + minute: '*' + hour: '*' + day_of_month: '*' + day_of_week: '*' + month: '*' + machine: www103 diff --git a/Tests/features/echo.feature b/Tests/features/echo.feature new file mode 100644 index 0000000..ee4b336 --- /dev/null +++ b/Tests/features/echo.feature @@ -0,0 +1,29 @@ +Feature: Echoing crontab + As a Developer + I want to echo crontab from app +# +# Scenario: +# When I run echo command with ini type +# Then The exit code should be 0 +# And The display should contain: +# """ +#Your crontab will look like: +##WARNING!!! +##This crontab file it at least partially managed by Crontab by Hexmedia, please check all restrictions that comes with that library at: https://github.com/Hexmedia/Crontab/blob/master/README.md +##EOT +# +# +# +## ------------ CURRENTLY MANAGED by Test -------------- +# +##DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5341b9a85da40865f5647c +## +#MAILTO=test@test.pl +#*/13 * * * * ./some/command1 > ./logs/some_log1.log +##DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5f6034d349bc599edc77a3 +## +#MAILTO=test@test.com +#NO_VALIDATE=1 +#* * * * * ./some/command2 > ./logs/some_log2.log +# """ +# diff --git a/Tests/features/synchronzie.feature b/Tests/features/synchronzie.feature new file mode 100644 index 0000000..a8e44e4 --- /dev/null +++ b/Tests/features/synchronzie.feature @@ -0,0 +1,233 @@ +Feature: Synchronization of crontab + As a Developer + I want to synchronize my cron with application configuration + + Background: + Given The process builder is fake + And Crontab save will be called + + Scenario Outline: Synchronizing with different types + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/test." and options: + """ + -t + """ + Then The exit code should be 0 + And The display should contain: + """ + Your crontab has been updated! + """ + And Crontab should be: + """ + #WARNING!!! + #This crontab file it at least partially managed by Crontab by Hexmedia, please check all restrictions that comes with that library at: https://github.com/Hexmedia/Crontab/blob/master/README.md + #EOT + + + */10 * * * * test + + # ------------ CURRENTLY MANAGED by Test -------------- + + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5341b9a85da40865f5647c + # + MAILTO=test@test.pl + */13 * * * * ./some/command1 > ./logs/some_log1.log + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5f6034d349bc599edc77a3 + # + MAILTO=test@test.com + NO_VALIDATE=1 + * * * * * ./some/command2 > ./logs/some_log2.log + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f563c3f5f02e473809fe5c1 + # + * * * * * ./some/command_without_log_file + + """ + + Examples: + | type | ext | + | ini | ini | + | yaml | yml | + | xml | xml | + | json | json | + | yml | yml | + + Scenario Outline: Working without crontab + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + """ + When I run synchronize command with file "./Tests/example_configurations/test." and options: + """ + -t + """ + Then The exit code should be 0 + And The display should contain: + """ + Your crontab has been updated! + """ + And Crontab should be: + """ + #WARNING!!! + #This crontab file it at least partially managed by Crontab by Hexmedia, please check all restrictions that comes with that library at: https://github.com/Hexmedia/Crontab/blob/master/README.md + #EOT + + + + # ------------ CURRENTLY MANAGED by Test -------------- + + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5341b9a85da40865f5647c + # + MAILTO=test@test.pl + */13 * * * * ./some/command1 > ./logs/some_log1.log + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5f6034d349bc599edc77a3 + # + MAILTO=test@test.com + NO_VALIDATE=1 + * * * * * ./some/command2 > ./logs/some_log2.log + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f563c3f5f02e473809fe5c1 + # + * * * * * ./some/command_without_log_file + + """ + + Examples: + | type | ext | + | ini | ini | + | yaml | yml | + | xml | xml | + | json | json | + | yml | yml | + + Scenario: It is not updating when there are no managed + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/empty.xml" and options: + """ + --type=xml + """ + Then The exit code should be 0 + And The display should contain: + """ + Your crontab does not need to be updated! + + """ + + @not_ready + Scenario: I run synchronize with unexisting file + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/test.dne" and options: + """ + --type=xml + """ + Then The exit code should be 1 + And The display should contain: + """ + File "./Tests/example_configurations/test.dne" does not exists! + """ + + @test + Scenario Outline: I run synchronize with wrong type + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/test." and options: + """ + --type= + """ + Then The exit code should be 1 + And The display should contain: + """ + ./Tests/example_configurations/test.' does not have --type= or has error in formatting. + """ + + Examples: + | type | ext | + | xml | yml | + | ini | xml | + | yml | json | + | json | ini | + + @not_ready + Scenario Outline: I run synchronize without giving type + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/test." + Then The exit code should be 0 + And The display should contain: + """ + Your crontab has been updated! + """ + And Crontab should be: + """ + #WARNING!!! + #This crontab file it at least partially managed by Crontab by Hexmedia, please check all restrictions that comes with that library at: https://github.com/Hexmedia/Crontab/blob/master/README.md + #EOT + + + */10 * * * * test + + # ------------ CURRENTLY MANAGED by Test -------------- + + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5341b9a85da40865f5647c + # + MAILTO=test@test.pl + */13 * * * * ./some/command1 > ./logs/some_log1.log + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f5f6034d349bc599edc77a3 + # + MAILTO=test@test.com + NO_VALIDATE=1 + * * * * * ./some/command2 > ./logs/some_log2.log + #DO NOT MODIFY! This task is managed by Crontab library by Hexmedia 0cbc6611f563c3f5f02e473809fe5c1 + # + * * * * * ./some/command_without_log_file + + """ + Examples: + | ext | + | ini | + | yml | + | xml | + | json | + | yml | + + @not_ready @desc_not_ready + Scenario: Dry run + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/test." and options: + """ + --dry-run + """ + Then The exit code should be 0 + + @not_ready @desc_not_ready + Scenario: With username + Given "'crontab' '-l'" command will have 0 as exit code and will return: + """ + */10 * * * * test + + """ + When I run synchronize command with file "./Tests/example_configurations/test." and options: + """ + --user=kuczek + """ + Then The exit code should be 0 + diff --git a/Tests/spec/ApplicationSpec.php b/Tests/spec/ApplicationSpec.php new file mode 100644 index 0000000..35c2ade --- /dev/null +++ b/Tests/spec/ApplicationSpec.php @@ -0,0 +1,27 @@ +shouldHaveType('Hexmedia\Crontab\Application'); + $this->shouldImplement('Symfony\Component\Console\Application'); + } + + function it_has_added_all_commands() + { + $this->all()->shouldHaveCount(5); + } +} diff --git a/Tests/spec/CrontabSpec.php b/Tests/spec/CrontabSpec.php index adfe7a7..2c910d0 100755 --- a/Tests/spec/CrontabSpec.php +++ b/Tests/spec/CrontabSpec.php @@ -12,6 +12,13 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; +/** + * Class CrontabSpec + * + * @package spec\Hexmedia\Crontab + * + * @covers Hexmedia\Crontab\Crontab + */ class CrontabSpec extends ObjectBehavior { public $name = "aaa"; diff --git a/Tests/spec/Parser/Ini/AustinHydeParserSpec.php b/Tests/spec/Parser/Ini/AustinHydeParserSpec.php index 58cdd08..9954750 100644 --- a/Tests/spec/Parser/Ini/AustinHydeParserSpec.php +++ b/Tests/spec/Parser/Ini/AustinHydeParserSpec.php @@ -1,11 +1,21 @@ + * @copyright 2013-2016 Hexmedia.pl + * @license @see LICENSE + */ namespace spec\Hexmedia\Crontab\Parser\Ini; -use dev\Hexmedia\Crontab\PhpSpec\Parser\IniParserObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\Parser\AbstractIniParserObjectBehavior; use Prophecy\Argument; -class AustinHydeParserSpec extends IniParserObjectBehavior +/** + * Class AustinHydeParserSpec + * + * @package spec\Hexmedia\Crontab\Parser\Ini + */ +class AustinHydeParserSpec extends AbstractIniParserObjectBehavior { function it_is_initializable() { diff --git a/Tests/spec/Parser/Ini/ParserFactorySpec.php b/Tests/spec/Parser/Ini/ParserFactorySpec.php index 6cbe039..38fd68e 100644 --- a/Tests/spec/Parser/Ini/ParserFactorySpec.php +++ b/Tests/spec/Parser/Ini/ParserFactorySpec.php @@ -1,10 +1,20 @@ + * @copyright 2013-2016 Hexmedia.pl + * @license @see LICENSE + */ namespace spec\Hexmedia\Crontab\Parser\Ini; use dev\Hexmedia\Crontab\PhpSpec\Parser\FactoryObjectBehavior; use Prophecy\Argument; +/** + * Class ParserFactorySpec + * + * @package spec\Hexmedia\Crontab\Parser\Ini + */ class ParserFactorySpec extends FactoryObjectBehavior { protected function getType() @@ -19,6 +29,10 @@ protected function getWorkingParser() protected function getParsersCount() { + if (defined("HHVM_VERSION")) { + return 1; + } + return 3; } } diff --git a/Tests/spec/Parser/Ini/Zend2ParserSpec.php b/Tests/spec/Parser/Ini/Zend2ParserSpec.php index 669843b..6f6e9d8 100644 --- a/Tests/spec/Parser/Ini/Zend2ParserSpec.php +++ b/Tests/spec/Parser/Ini/Zend2ParserSpec.php @@ -1,12 +1,31 @@ + * @copyright 2013-2016 Hexmedia.pl + * @license @see LICENSE + */ namespace spec\Hexmedia\Crontab\Parser\Ini; -use dev\Hexmedia\Crontab\PhpSpec\Parser\IniParserObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\Parser\AbstractIniParserObjectBehavior; +use PhpSpec\Exception\Example\SkippingException; use Prophecy\Argument; -class Zend2ParserSpec extends IniParserObjectBehavior +/** + * Class Zend2ParserSpec + * + * @package spec\Hexmedia\Crontab\Parser\Ini + */ +class Zend2ParserSpec extends AbstractIniParserObjectBehavior { + function let() { + if (defined('HHVM_VERSION')) { + throw new SkippingException("Zend2 is not compatible with HHVM."); + } + + parent::let(); + } + function it_is_initializable() { $this->shouldHaveType('Hexmedia\Crontab\Parser\Ini\Zend2Parser'); diff --git a/Tests/spec/Parser/Ini/ZendParserSpec.php b/Tests/spec/Parser/Ini/ZendParserSpec.php index feb9923..c241118 100644 --- a/Tests/spec/Parser/Ini/ZendParserSpec.php +++ b/Tests/spec/Parser/Ini/ZendParserSpec.php @@ -1,12 +1,31 @@ + * @copyright 2013-2016 Hexmedia.pl + * @license @see LICENSE + */ namespace spec\Hexmedia\Crontab\Parser\Ini; -use dev\Hexmedia\Crontab\PhpSpec\Parser\IniParserObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\Parser\AbstractIniParserObjectBehavior; +use PhpSpec\Exception\Example\SkippingException; use Prophecy\Argument; -class ZendParserSpec extends IniParserObjectBehavior +/** + * Class ZendParserSpec + * + * @package spec\Hexmedia\Crontab\Parser\Ini + */ +class ZendParserSpec extends AbstractIniParserObjectBehavior { + function let() { + if (defined('HHVM_VERSION')) { + throw new SkippingException("Zend is not compatible with HHVM."); + } + + parent::let(); + } + function it_is_initializable() { $this->shouldHaveType('Hexmedia\Crontab\Parser\Ini\ZendParser'); diff --git a/Tests/spec/Parser/Json/ParserFactorySpec.php b/Tests/spec/Parser/Json/ParserFactorySpec.php new file mode 100644 index 0000000..b24c577 --- /dev/null +++ b/Tests/spec/Parser/Json/ParserFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Hexmedia\Crontab\Parser\Json\PhpParser'); + } +} diff --git a/Tests/spec/Parser/Unix/ParserFactorySpec.php b/Tests/spec/Parser/Unix/ParserFactorySpec.php index 98d5870..971f211 100644 --- a/Tests/spec/Parser/Unix/ParserFactorySpec.php +++ b/Tests/spec/Parser/Unix/ParserFactorySpec.php @@ -37,7 +37,7 @@ function it_returns_correct_praser() { $this->isSystemSupported(); - $this->create('')->shouldImplement('Hexmedia\Crontab\Parser\ParserInterface'); + $this->create('', '/tmp/file')->shouldImplement('Hexmedia\Crontab\Parser\ParserInterface'); } function it_is_constructing_with_prefered_full_name() @@ -45,7 +45,7 @@ function it_is_constructing_with_prefered_full_name() $this->isSystemSupported(); $this->beConstructedWith($this->getFullWorkingParserName()); - $this->create("[some]")->shouldHaveType($this->getFullWorkingParserName()); + $this->create('[some]', '/tmp/file')->shouldHaveType($this->getFullWorkingParserName()); } function it_is_constructing_with_prefered_only_class_name() @@ -53,7 +53,7 @@ function it_is_constructing_with_prefered_only_class_name() $this->isSystemSupported(); $this->beConstructedWith($this->getWorkingParser()); - $this->create("[some]")->shouldHaveType($this->getFullWorkingParserName()); + $this->create('[some]', '/tmp/file')->shouldHaveType($this->getFullWorkingParserName()); } } diff --git a/Tests/spec/Parser/Unix/UnixParserSpec.php b/Tests/spec/Parser/Unix/UnixParserSpec.php index 5f8eecb..a637397 100644 --- a/Tests/spec/Parser/Unix/UnixParserSpec.php +++ b/Tests/spec/Parser/Unix/UnixParserSpec.php @@ -21,7 +21,7 @@ function let() $content = file_get_contents($file); - $this->beConstructedWith($content); + $this->beConstructedWith($content, null); } function it_is_not_working_with_wrong_file() @@ -30,7 +30,7 @@ function it_is_not_working_with_wrong_file() $content = file_get_contents($file); - $this->beConstructedWith($content); + $this->beConstructedWith($content, null); $this->shouldThrow(new ParseException('Cannot match this file error: \'wrong file format\''))->duringParse(); } @@ -97,4 +97,11 @@ function it_is_loading_file_correctly() $parsed[$i]['comment']->shouldReturn($comments[$i]); } } + + function it_is_returning_null_when_there_is_no_system_crontab() + { + $this->beConstructedWith(false, null); + + $this->parse()->shouldReturn(null); + } } diff --git a/Tests/spec/Parser/Xml/ParserFactorySpec.php b/Tests/spec/Parser/Xml/ParserFactorySpec.php new file mode 100644 index 0000000..49d0ef3 --- /dev/null +++ b/Tests/spec/Parser/Xml/ParserFactorySpec.php @@ -0,0 +1,24 @@ +shouldHaveType('Hexmedia\Crontab\Parser\Xml\PhpParser'); + } +} diff --git a/Tests/spec/Parser/Yaml/ParserFactorySpec.php b/Tests/spec/Parser/Yaml/ParserFactorySpec.php index 62403d6..898f1d1 100644 --- a/Tests/spec/Parser/Yaml/ParserFactorySpec.php +++ b/Tests/spec/Parser/Yaml/ParserFactorySpec.php @@ -24,6 +24,10 @@ protected function getWorkingParser() protected function getParsersCount() { + if (defined("HHVM_VERSION")) { + return 1; + } + return 2; } } diff --git a/Tests/spec/Parser/Yaml/SymfonyParserSpec.php b/Tests/spec/Parser/Yaml/SymfonyParserSpec.php index 5567651..67e321c 100644 --- a/Tests/spec/Parser/Yaml/SymfonyParserSpec.php +++ b/Tests/spec/Parser/Yaml/SymfonyParserSpec.php @@ -2,10 +2,10 @@ namespace spec\Hexmedia\Crontab\Parser\Yaml; -use dev\Hexmedia\Crontab\PhpSpec\Parser\YamlParserObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\Parser\AbstractYamlParserObjectBehavior; use Prophecy\Argument; -class SymfonyParserSpec extends YamlParserObjectBehavior +class SymfonyParserSpec extends AbstractYamlParserObjectBehavior { } diff --git a/Tests/spec/Parser/Yaml/ZendParserSpec.php b/Tests/spec/Parser/Yaml/ZendParserSpec.php index 33d8fde..cd4bcd6 100644 --- a/Tests/spec/Parser/Yaml/ZendParserSpec.php +++ b/Tests/spec/Parser/Yaml/ZendParserSpec.php @@ -2,10 +2,17 @@ namespace spec\Hexmedia\Crontab\Parser\Yaml; -use dev\Hexmedia\Crontab\PhpSpec\Parser\YamlParserObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\Parser\AbstractYamlParserObjectBehavior; +use PhpSpec\Exception\Example\SkippingException; use Prophecy\Argument; -class ZendParserSpec extends YamlParserObjectBehavior +class ZendParserSpec extends AbstractYamlParserObjectBehavior { + function let() { + if (defined('HHVM_VERSION')) { + throw new SkippingException("Zend is not compatible with HHVM."); + } + parent::let(); + } } diff --git a/Tests/spec/Reader/SystemReaderSpec.php b/Tests/spec/Reader/SystemReaderSpec.php index 4b32f11..f176fd5 100644 --- a/Tests/spec/Reader/SystemReaderSpec.php +++ b/Tests/spec/Reader/SystemReaderSpec.php @@ -11,11 +11,18 @@ use Hexmedia\Crontab\Exception\ClassNotExistsException; use Prophecy\Argument; +/** + * Class SystemReaderSpec + * + * @package spec\Hexmedia\Crontab\Reader + * + * @covers Hexmedia\Crontab\Reader\SystemReader + */ class SystemReaderSpec extends SystemAwareObjectBehavior { function let() { - $this->beConstructedWith("kuczek", null); + $this->beConstructedWith(null, null); } function it_is_initializable() diff --git a/Tests/spec/Reader/UnixFileReaderSpec.php b/Tests/spec/Reader/UnixFileReaderSpec.php index 6546e64..0073c84 100644 --- a/Tests/spec/Reader/UnixFileReaderSpec.php +++ b/Tests/spec/Reader/UnixFileReaderSpec.php @@ -7,14 +7,15 @@ namespace spec\Hexmedia\Crontab\Reader; -use PhpSpec\ObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\SystemAwareObjectBehavior; use Prophecy\Argument; -class UnixFileReaderSpec extends ObjectBehavior +class UnixFileReaderSpec extends SystemAwareObjectBehavior { function let() { - $this->beConstructedWith("./Test/example_configurations/test.unix"); + $this->isSystemSupported(); + $this->beConstructedWith("./Tests/example_configurations/test.unix"); } function it_is_initializable() diff --git a/Tests/spec/Reader/UnixSystemReaderSpec.php b/Tests/spec/Reader/UnixSystemReaderSpec.php index 40c2db8..aa01f10 100644 --- a/Tests/spec/Reader/UnixSystemReaderSpec.php +++ b/Tests/spec/Reader/UnixSystemReaderSpec.php @@ -7,7 +7,7 @@ namespace spec\Hexmedia\Crontab\Reader; -use PhpSpec\ObjectBehavior; +use dev\Hexmedia\Crontab\PhpSpec\SystemAwareObjectBehavior; use Prophecy\Argument; /** @@ -20,11 +20,12 @@ * @method static $this removeSupportedOs(string $name) * @method static array getSupportedOses() */ -class UnixSystemReaderSpec extends ObjectBehavior +class UnixSystemReaderSpec extends SystemAwareObjectBehavior { function let() { - $this->beConstructedWith("kuczek", null); + $this->isSystemSupported(); + $this->beConstructedWith(null, null); } function it_is_initializable() diff --git a/Tests/spec/Reader/YamlReaderSpec.php b/Tests/spec/Reader/YamlReaderSpec.php index 048abf2..353a1d5 100644 --- a/Tests/spec/Reader/YamlReaderSpec.php +++ b/Tests/spec/Reader/YamlReaderSpec.php @@ -15,7 +15,7 @@ class YamlReaderSpec extends ObjectBehavior { function let(Crontab $crontab) { - $file = "./Tests/example_configurations/test.yaml"; + $file = "./Tests/example_configurations/test.yml"; $machine = "www101"; $this->beConstructedWith($file, $crontab, $machine); } diff --git a/Tests/spec/ReaderFactorySpec.php b/Tests/spec/ReaderFactorySpec.php index 55e6970..5a97f0f 100644 --- a/Tests/spec/ReaderFactorySpec.php +++ b/Tests/spec/ReaderFactorySpec.php @@ -10,6 +10,13 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; +/** + * Class ReaderFactorySpec + * + * @package spec\Hexmedia\Crontab + * + * @covers Hexmedia\Crontab\ReaderFactory + */ class ReaderFactorySpec extends ObjectBehavior { function it_is_initializable() diff --git a/Tests/spec/System/UnixSpec.php b/Tests/spec/System/UnixSpec.php index 2d89698..63d4239 100644 --- a/Tests/spec/System/UnixSpec.php +++ b/Tests/spec/System/UnixSpec.php @@ -33,11 +33,21 @@ function let(ProcessBuilder $processBuilder, Process $process) $this::setProcessBuilder($processBuilder); } + function letGo() + { + $this::setProcessBuilder(null); + } + function it_is_initializable() { $this->shouldHaveType('Hexmedia\Crontab\System\Unix'); } + function it_has_default_porcess_builder() + { + $this::getProcessBuilder()->shouldHaveType('Symfony\Component\Process\ProcessBuilder'); + } + function it_does_not_allow_to_add_second_time() { $this::addUnix("Foo")->shouldReturn(true); diff --git a/Tests/spec/TaskSpec.php b/Tests/spec/TaskSpec.php index 78ead9c..459fb55 100644 --- a/Tests/spec/TaskSpec.php +++ b/Tests/spec/TaskSpec.php @@ -11,6 +11,13 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; +/** + * Class TaskSpec + * + * @package spec\Hexmedia\Crontab + * + * @covers Hexmedia\Reader\Task + */ class TaskSpec extends ObjectBehavior { function it_is_initializable() diff --git a/Tests/spec/VariablesSpec.php b/Tests/spec/VariablesSpec.php index fde3f07..900d0a4 100644 --- a/Tests/spec/VariablesSpec.php +++ b/Tests/spec/VariablesSpec.php @@ -11,6 +11,13 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; +/** + * Class VariablesSpec + * + * @package spec\Hexmedia\Crontab + * + * @covers Hexmedia\Variables + */ class VariablesSpec extends ObjectBehavior { function let() diff --git a/Tests/spec/Writer/System/UnixWriterSpec.php b/Tests/spec/Writer/System/UnixWriterSpec.php index af702b2..b430109 100644 --- a/Tests/spec/Writer/System/UnixWriterSpec.php +++ b/Tests/spec/Writer/System/UnixWriterSpec.php @@ -7,15 +7,44 @@ namespace spec\Hexmedia\Crontab\Writer\System; +use dev\Hexmedia\Crontab\PhpSpec\SystemAwareObjectBehavior; use Hexmedia\Crontab\Crontab; +use Hexmedia\Crontab\System\Unix; use Hexmedia\Crontab\Task; use Hexmedia\Crontab\Variables; -use PhpSpec\ObjectBehavior; +use Hexmedia\Symfony\FakeProcess\FakeProcessBuilder; +use PhpSpec\Exception\Example\FailureException; use Prophecy\Argument; -class UnixWriterSpec extends ObjectBehavior +class UnixWriterSpec extends SystemAwareObjectBehavior { - private function prepareTask(&$task, $variables, $notManaged = false) + private $defaultContent = << some_log_file.log +#This is some comment with +#two lines +*/10 * * * * test > some_log_file.log + +# ------------ CURRENTLY MANAGED by TestAopTest -------------- + +#DO NOT MODIFY! This task is managed by Crontab library by Hexmedia bb83a7fd849c0ab6ec0cfb38d3db6a2 +#This is some comment with +#two lines +*/10 * * * * test > some_log_file.log +#DO NOT MODIFY! This task is managed by Crontab library by Hexmedia bb83a7fd849c0ab6ec0cfb38d3db6a2 +#This is some comment with +#two lines +*/10 * * * * test > some_log_file.log + +CONTENT; + + private function prepareTask(&$task, $variables, $notManaged = false, $comment = true) { $task->getCommand()->willReturn("test"); $task->getMonth()->willReturn("*"); @@ -24,7 +53,7 @@ private function prepareTask(&$task, $variables, $notManaged = false) $task->getHour()->willReturn("*"); $task->getMinute()->willReturn("*/10"); $task->getLogFile()->willReturn("some_log_file.log"); - $task->getBeforeComment()->willReturn("This is some comment with \n two lines"); + $task->getBeforeComment()->willReturn($comment ? "This is some comment with \ntwo lines" : ''); $task->isNotManaged()->willReturn($notManaged); $task->getVariables()->willReturn($variables); $task->getName()->willReturn("synchronize1"); @@ -32,6 +61,8 @@ private function prepareTask(&$task, $variables, $notManaged = false) function let(Crontab $crontab, Task $nmTask1, Task $nmTask2, Task $task1, Task $task2, Variables $variables1) { + $this->isSystemSupported(); + $this->prepareTask($task1, $variables1); $this->prepareTask($task2, $variables1); @@ -50,41 +81,11 @@ function it_is_initializable() $this->shouldImplement('Hexmedia\Crontab\Writer\System\WriterInterface'); } -// function it_is_able_to_write($crontab) -// { -// $this->write($crontab)->shouldReturn(true); -// } -// function it_is_able_to_get_content($crontab) { - $shouldBe = << some_log_file.log -#This is some comment with -# two lines -*/10 * * * * test > some_log_file.log - -# ------------ CURRENTLY MANAGED by TestAopTest -------------- - -#DO NOT MODIFY! This task is managed by Crontab library by Hexmedia bb83a7fd849c0ab6ec0cfb38d3db6a2 -#This is some comment with -# two lines -*/10 * * * * test > some_log_file.log -#DO NOT MODIFY! This task is managed by Crontab library by Hexmedia bb83a7fd849c0ab6ec0cfb38d3db6a2 -#This is some comment with -# two lines -*/10 * * * * test > some_log_file.log - -CONTENT; $this ->getContent($crontab) - ->shouldReturn($shouldBe); + ->shouldReturn($this->defaultContent); } function it_has_support_for_variables(Task $task3, Variables $variables3, Crontab $crontab2) @@ -94,7 +95,7 @@ function it_has_support_for_variables(Task $task3, Variables $variables3, Cronta $variables3->current()->willReturn('value'); $variables3->key()->willReturn('key'); $variables3->rewind()->shouldBeCalled(); - $variables3->valid()->willReturn(true,false); + $variables3->valid()->willReturn(true, false); $variables3->next()->shouldBeCalled(); $crontab2->getName()->willReturn("TestAopTest"); @@ -107,7 +108,7 @@ function it_has_support_for_variables(Task $task3, Variables $variables3, Cronta ->shouldReturn( << some_log_file.log CONTENT ); } + + function it_works_with_crons_without_comments(Task $taskWC, Variables $variablesWC, Crontab $crontabWC) + { + $this->prepareTask($taskWC, $variablesWC, true, false); + + $variablesWC->current()->willReturn('value'); + $variablesWC->key()->willReturn('key'); + $variablesWC->rewind()->shouldBeCalled(); + $variablesWC->valid()->willReturn(true, false); + $variablesWC->next()->shouldBeCalled(); + + $crontabWC->getName()->willReturn("TestAopTest"); + + $crontabWC->getManagedTasks()->willReturn(array($taskWC)); + $crontabWC->getNotManagedTasks()->willReturn(array()); + +// echo($this->getContent($crontabWC)->getWrappedObject()); +// die(); + + $this + ->getContent($crontabWC) + ->shouldReturn( + << some_log_file.log + +CONTENT + ); + } + + function it_allows_to_write($crontab) + { + $processBuilder = new FakeProcessBuilder(); + + $shouldBe = $this->defaultContent; + + $processBuilder->addCommand( + "'crontab' '(/var)?/tmp/.*'", + function ($command) use ($shouldBe) { + if (preg_match("#'crontab' '((/var)?/tmp/.*)'#", $command, $matches)) { + $content = file_get_contents($matches[1]); + + if ($content !== $shouldBe) { + throw new FailureException("Content in not correct"); + } + } + }, + 1 + ); + + Unix::setProcessBuilder($processBuilder); + + $this->write($crontab)->shouldReturn(true); + + Unix::setProcessBuilder(null); + } } diff --git a/behat.yml b/behat.yml new file mode 100644 index 0000000..891f938 --- /dev/null +++ b/behat.yml @@ -0,0 +1,38 @@ +default: + autoload: + '': %paths.base%/Tests/features/bootstrap + suites: + default: + contexts: [ 'dev\Hexmedia\Crontab\Behat\ApplicationContext' ] + paths: [ %paths.base%/Tests/features ] + formatters: + pretty: true + filters: + tags: ~@not_ready + all: + contexts: [ 'dev\Hexmedia\Crontab\Behat\ApplicationContext' ] + paths: [ %paths.base%/Tests/features ] + formatters: + pretty: true + + travis: + contexts: [ 'dev\Hexmedia\Crontab\Behat\ApplicationContext' ] + paths: [ %paths.base%/Tests/features_travis ] + formatters: + pretty: true + filters: + tags: ~@not_ready + + formatters: + html: + output_path: %paths.base%/build/report/behat + pretty: ~ + + extensions: + emuse\BehatHTMLFormatter\BehatHTMLFormatterExtension: + name: html + renderer: Twig + file_name: index + print_args: true + print_outp: true + loop_break: true diff --git a/bin/crontab b/bin/crontab index 720df60..7390e5f 100755 --- a/bin/crontab +++ b/bin/crontab @@ -1,13 +1,8 @@ #!/usr/bin/env php - add(new \Hexmedia\Crontab\Console\SynchronizeCommand()); -$application->add(new \Hexmedia\Crontab\Console\EchoCommand()); -$application->add(new \Hexmedia\Crontab\Console\ClearCommand()); +$application = new \Hexmedia\Crontab\Application(); $application->run(); diff --git a/build.xml b/build.xml index ff446ee..d722074 100644 --- a/build.xml +++ b/build.xml @@ -13,6 +13,7 @@ + @@ -69,17 +70,17 @@ - + - + - + @@ -95,6 +96,7 @@ + @@ -151,6 +153,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -345,7 +371,7 @@ - + @@ -353,9 +379,9 @@ - - + + - + diff --git a/composer.json b/composer.json index fddfe66..5fc8297 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,10 @@ "seld/jsonlint": "^1.4", "hexmedia/yaml-linter": ">0.1,<2", "austinhyde/iniparser": "~1.0@dev", - "hexmedia/code-style": "~1.0@dev" + "hexmedia/code-style": "~1.0@dev", + "behat/behat": "~3.0", + "kuczek/behat-html-formatter": ">=0.1.2,<0.2", + "hexmedia/symfony-fake-process": ">2.0@dev,<4.0@dev" }, "suggest": { "symfony/yaml": "Enables support for yaml configuration files.", diff --git a/doc/tasks.xsd b/doc/tasks.xsd new file mode 100644 index 0000000..939bd1b --- /dev/null +++ b/doc/tasks.xsd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src-dev/Behat/ApplicationContext.php b/src-dev/Behat/ApplicationContext.php new file mode 100644 index 0000000..0437e6b --- /dev/null +++ b/src-dev/Behat/ApplicationContext.php @@ -0,0 +1,276 @@ + + * @copyright 2013-2016 Hexmedia.pl + * @license @see LICENSE + */ + +namespace dev\Hexmedia\Crontab\Behat; + +use Behat\Behat\Context\Context; +use Behat\Behat\Context\SnippetAcceptingContext; +use Behat\Gherkin\Node\PyStringNode; +use Hexmedia\Crontab\Application; +use Hexmedia\Crontab\System\Unix; +use Hexmedia\Symfony\FakeProcess\FakeProcessBuilder; +use PhpSpec\Matcher\MatchersProviderInterface; +use Symfony\Component\Console\Tester\ApplicationTester; +use PHPUnit_Framework_Assert as Assertions; + +/** + * Class CommandFeatureContext + * + * @package dev\Hexmedia\Crontab\Behat + */ +class ApplicationContext implements Context, MatchersProviderInterface, SnippetAcceptingContext +{ + + /** + * @var ApplicationTester + */ + private $tester; + + /** + * @var Application + */ + private $application; + + /** + * @var int + */ + private $lastExitCode; + + /** + * @var string + */ + private $lastDisplay; + + /** + * @var FakeProcessBuilder + */ + private $processBuilder; + + /** + * @var string + */ + private $currentCrontab; + + /** + * @return array + */ + public function getMatchers() + { + return array(); + } + + /** + * @beforeScenario + */ + public function initApplication() + { + $this->application = new Application(); + + $this->application->setAutoExit(false); + + $this->tester = new ApplicationTester($this->application); + } + + /** + * @param string $command + * @param string $file + * @param PyStringNode $options + * + * @When /^I run (?P[a-zA-Z0-9\.\:]*) command with file \"(?P[^\"]*)\"$/ + * @When /^I run (?P[a-zA-Z0-9\.\:]*) command with file \"(?P[^\"]*)\" and options:$/ + */ + public function iRunCommand($command, $file = null, PyStringNode $options = null) + { + $arguments = array( + 'command' => $command, + 'name' => 'Test', + ); + + $arguments['configuration-file'] = __DIR__ . "/../../" . $file; + + $options = $this->parseOptions($options); + + $arguments += $options; + + $runOptions = array('interactive' => false, 'decorated' => false); + + $this->lastExitCode = $this->tester->run($arguments, $runOptions); + $this->lastDisplay = $this->tester->getDisplay(); + } + + /** + * @param int $response + * + * @throws \Exception + * + * @Then /The exit code should be (\d+)/ + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function theExitCodeShouldBe($response) + { + if ((int) $response !== $this->lastExitCode) { + throw new \Exception($this->lastDisplay); + } + + Assertions::assertSame((int) $response, $this->lastExitCode); + } + + /** + * @param PyStringNode $content + * + * @Then Crontab should contain: + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function crontabShouldContain(PyStringNode $content) + { + $crontab = $this->currentCrontab; + + Assertions::assertNotFalse($crontab); + + if ("" == $content->getRaw()) { + Assertions::assertEquals("", $crontab); + } else { + Assertions::assertContains($content->getRaw(), $crontab); + } + } + + /** + * @param PyStringNode $content + * + * @Then Crontab should be: + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function crontabShouldBe(PyStringNode $content) + { + $crontab = $this->currentCrontab; + + Assertions::assertNotFalse($crontab); + + if ("" == $content->getRaw()) { + Assertions::assertEquals("", $crontab); + } else { + Assertions::assertEquals($content->getRaw(), $crontab); + } + } + + /** + * @param PyStringNode $display + * + * @Then The display should be: + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function theDisplayShouldBe(PyStringNode $display) + { + Assertions::assertSame($display->getRaw(), $this->lastDisplay); + } + + /** + * @param PyStringNode $display + * + * @Then The display should contain: + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function theDisplayShouldContain(PyStringNode $display) + { + Assertions::assertContains($display->getRaw(), $this->lastDisplay); + } + + /** + * @Then The display should be empty + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function theDisplayShouldBeEmpty() + { + Assertions::assertEquals('', $this->lastDisplay); + } + + /** + * @Given The process builder is fake + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public function theProcessBuilderIsFake() + { + $this->processBuilder = new FakeProcessBuilder(); + + Unix::setProcessBuilder($this->processBuilder); + } + + /** + * @Given /^\"(?P[^\"]*)\" command will have (?P\d+) as exit code$/ + * @Given /^\"(?P[^\"]*)\" command will have (?P\d+) as exit code and will return:/ + * + * @param string $command + * @param string $code + * @param PyStringNode $string + */ + public function commandWillHaveAsExitCodeAndWillReturn($command, $code, PyStringNode $string = null) + { + $this->processBuilder->addCommand( + $command, + function ($command) use ($string) { + if (null !== $string) { + return $string->getRaw(); + } + + return ''; + }, + $code + ); + } + + /** + * @Given Crontab save will be called + */ + public function crontabSaveShouldBeCalled() + { + $command = "'crontab' '/tmp/.*'"; + + $content = ''; + $this->currentCrontab = &$content; + $content = '1'; + + $this->processBuilder->addCommand( + $command, + function ($command) use (&$content) { + if (preg_match("#'crontab' '(/tmp/.*)'#", $command, $matches)) { + $content = file_get_contents($matches[1]); + } + }, + 0 + ); + } + + /** + * @param PyStringNode $options + * + * @return array + */ + private function parseOptions(PyStringNode $options) + { + $return = array(); + + foreach ($options->getStrings() as $option) { + if (preg_match('/(?P