2 /*********************************************************************************
3 * SugarCRM Community Edition is a customer relationship management program developed by
4 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Affero General Public License version 3 as published by the
8 * Free Software Foundation with the addition of the following permission added
9 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
18 * You should have received a copy of the GNU Affero General Public License along with
19 * this program; if not, see http://www.gnu.org/licenses or write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU Affero General Public License version 3.
30 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by
32 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33 * technical reasons, the Appropriate Legal Notices must display the words
34 * "Powered by SugarCRM".
35 ********************************************************************************/
37 require_once 'include/SugarQueue/SugarJobQueue.php';
38 require_once 'modules/Schedulers/Scheduler.php';
40 class SchedulerTest extends Sugar_PHPUnit_Framework_TestCase
42 static protected $old_timedate;
44 public static function setUpBeforeClass()
46 self::$old_timedate = $GLOBALS['timedate'];
47 unset($GLOBALS['disable_date_format']);
48 $GLOBALS['current_user'] = SugarTestUserUtilities::createAnonymousUser();
49 $GLOBALS['current_user']->setPreference('datef', "m/d/Y");
50 $GLOBALS['current_user']->setPreference('timef', "h:ia");
51 $GLOBALS['current_user']->setPreference('timezone', "America/Los_Angeles");
54 public static function tearDownAfterClass()
56 SugarTestUserUtilities::removeAllCreatedAnonymousUsers();
57 unset($GLOBALS['current_user']);
58 $GLOBALS['timedate'] = self::$old_timedate;
61 public function setUp()
63 $this->scheduler = new TestScheduler(false);
64 $GLOBALS['timedate'] = $this->timedate = TimeDate::getInstance();
65 $this->timedate->allow_cache = true;
66 $this->now = $this->timedate->getNow();
69 public function tearDown()
71 $this->timedate->setNow($this->now);
72 $GLOBALS['db']->query("DELETE FROM schedulers WHERE id='{$this->scheduler->id}'");
73 $GLOBALS['db']->query("DELETE FROM job_queue WHERE scheduler_id='{$this->scheduler->id}'");
77 * Test catch-up functionality
79 public function testCatchUp()
81 $this->scheduler->job_interval = "*::*::*::*::*";
82 $this->scheduler->catch_up = true;
83 $this->assertTrue($this->scheduler->fireQualified());
84 // we were late to the job
85 $this->timedate->setNow($this->timedate->fromDb("2011-02-01 14:45:00"));
86 $this->scheduler->job_interval = "30::3::*::*::*"; // 10:30 or 11:30 in UTC
87 $this->scheduler->last_run = null;
88 $this->scheduler->catch_up = 0;
89 $this->assertFalse($this->scheduler->fireQualified());
90 // but we can still catch up
91 $this->scheduler->catch_up = 1;
92 $this->assertTrue($this->scheduler->fireQualified());
93 // if already did it, don't catch up
94 $this->scheduler->last_run = $this->timedate->getNow(true)->setDate(2011, 2, 1)->setTime(3, 30)->asDb();
95 $this->assertFalse($this->scheduler->fireQualified());
96 // but if did it yesterday, do
97 $this->scheduler->last_run = $this->timedate->getNow(true)->setDate(2011, 1, 31)->setTime(3, 30)->asDb();
98 $this->assertTrue($this->scheduler->fireQualified());
102 * Test date start/finish
104 public function testDateFromTo()
106 $this->scheduler->job_interval = "*::*::*::*::*";
107 $this->scheduler->catch_up = 0;
108 $this->timedate->setNow($this->timedate->fromDb("2011-04-17 20:00:00"));
111 $this->assertTrue($this->scheduler->fireQualified());
112 // limit start, inclusive
113 $this->scheduler->date_time_start = "2011-01-01 20:00:00";
114 $this->assertTrue($this->scheduler->fireQualified(), "Inclusive start test failed");
116 $this->scheduler->date_time_start = "2011-04-17 20:00:00";
117 $this->assertTrue($this->scheduler->fireQualified(), "Start now test failed");
118 // limit start, exclusive
119 $this->scheduler->date_time_start = "2011-05-01 20:00:00";
120 $this->assertFalse($this->scheduler->fireQualified(), "Exclusive start test failed");
122 // limit end, inclusive
123 $this->scheduler->date_time_start = "2011-01-01 20:00:00";
124 $this->scheduler->date_time_end = "2011-05-01 20:00:00";
125 $this->assertTrue($this->scheduler->fireQualified(), "Inclusive start test failed");
127 $this->scheduler->date_time_end = "2011-04-17 20:00:00";
128 $this->assertTrue($this->scheduler->fireQualified(), "Start now test failed");
129 // limit start, exclusive
130 $this->scheduler->date_time_end = "2011-02-01 20:00:00";
131 $this->assertFalse($this->scheduler->fireQualified(), "Exclusive start test failed");
135 * Test date start/finish
137 public function testActiveFromTo()
139 $this->scheduler->job_interval = "*::*::*::*::*";
140 $this->scheduler->catch_up = 0;
141 $this->scheduler->time_from = "02:00:00";
142 $this->scheduler->time_to = "21:00:00";
144 $this->timedate->setNow($this->timedate->fromUser("1/17/2011 01:20am"));
145 $this->assertFalse($this->scheduler->fireQualified(), "Before start test failed");
146 $this->timedate->setNow($this->timedate->fromUser("2/17/2011 02:00am"));
147 $this->assertTrue($this->scheduler->fireQualified(), "Start test failed");
148 $this->timedate->setNow($this->timedate->fromUser("5/17/2011 10:00am"));
149 $this->assertTrue($this->scheduler->fireQualified(), "After start test failed");
150 $this->timedate->setNow($this->timedate->fromUser("7/17/2011 9:00pm"));
151 $this->assertTrue($this->scheduler->fireQualified(), "End test failed");
152 $this->timedate->setNow($this->timedate->fromUser("11/17/2011 11:30pm"));
153 $this->assertFalse($this->scheduler->fireQualified(), "After end test failed");
156 public function getSchedules()
159 // schedule - now point - last run - should be run?
160 array("*::*::*::*::*", "5/17/2011 1:00am", null, true),
161 array("*::*::*::*::*", "5/17/2011 11:20pm", null, true),
162 array("*::*::*::*::*", "5/17/2011 5:40pm", null, true),
163 array("*::*::*::*::*", "5/17/2011 7:43pm", null, true),
165 array("25::*::*::*::*", "5/17/2011 5:25pm", null, true),
166 array("25::*::*::*::*", "5/17/2011 7:27pm", null, false),
167 array("25::*::*::*::*", "5/17/2011 7:25pm", "5/17/2011 7:25pm", false),
168 array("25::*::*::*::*", "5/17/2011 8:25pm", "5/17/2011 7:25pm", true),
170 array("0::6::*::*::*", "5/17/2011 6:00pm", null, false),
171 array("0::6::*::*::*", "5/17/2011 6:00am", null, true),
172 array("0::6::*::*::*", "5/17/2011 1:00pm", null, false),
173 array("0::6::*::*::*", "5/17/2011 2:00pm", null, false),
175 array("0::2::1::*::*", "2/1/2011 2:00pm", null, false),
176 array("0::2::1::*::*", "2/1/2011 2:00am", null, true),
177 array("0::2::1::*::*", "2/17/2011 2:00am", null, false),
178 array("0::2::1::*::*", "1/31/2011 2:00am", null, false),
179 array("0::2::1::*::*", "2/2/2011 2:00am", null, false),
180 // Every 15 mins on Mon, Tue
181 array("*/15::*::*::*::1,2", "5/16/2011 2:00pm", null, true),
182 array("*/15::*::*::*::1,2", "5/17/2011 2:00pm", null, true),
183 array("*/15::*::*::*::1,2", "5/18/2011 2:00pm", null, false),
184 array("*/15::*::*::*::1,2", "5/17/2011 2:10pm", "5/17/2011 2:00pm", false),
185 array("*/15::*::*::*::1,2", "5/17/2011 2:15pm", "5/17/2011 2:00pm", true),
186 // Job with incorrectly set time-range should fail to execute (crontab notation allows no reverse ranges)
187 array("1-59::*::*::*::*", "5/17/2011 2:15pm", null, true),
188 array("59-1::*::*::*::*", "5/17/2011 2:15pm", null, false),
193 * @dataProvider getSchedules
194 * Test deriveDBDateTimes()
196 public function testDbTimes($sched, $time, $last, $run)
198 $time = $this->timedate->fromUser($time);
199 $time->setTime($time->hour, $time->min, rand(0, 59));
200 $this->timedate->setNow($time);
201 $this->scheduler->job_interval = $sched;
202 $this->scheduler->catch_up = false;
204 $this->scheduler->last_run = $this->timedate->fromUser($last)->asDb();
206 $this->scheduler->last_run = null;
209 $this->assertTrue($this->scheduler->fireQualified());
211 $this->assertFalse($this->scheduler->fireQualified());
215 public function testScheduleJob()
217 $this->scheduler->job_interval = "*::*::*::*::*";
218 $this->scheduler->new_with_id = true;
219 $this->scheduler->status = "Active";
220 $this->scheduler->job = "test::test";
221 $this->scheduler->save();
222 $queue = new MockSchedulerQueue();
223 $this->scheduler->checkPendingJobs($queue);
224 $this->assertNotEmpty($queue->jobs, "Job was not submitted");
226 foreach($queue->jobs as $job) {
227 if($job->scheduler_id == $this->scheduler->id) {
232 $this->assertNotEmpty($ourjob, "Could not find our job in the queue");
233 $this->assertEquals(SchedulersJob::JOB_STATUS_QUEUED, $ourjob->status, "Wrong status");
236 public function testScheduleJobRepeat()
238 $this->scheduler->job_interval = "*::*::*::*::*";
239 $this->scheduler->job = "test::test";
240 $this->scheduler->status = "Active";
241 $this->scheduler->new_with_id = true;
242 $this->scheduler->save();
243 $queue = new MockSchedulerQueue();
244 $this->scheduler->checkPendingJobs($queue);
245 $this->assertNotEmpty($queue->jobs, "Job was not submitted");
247 foreach($queue->jobs as $job) {
248 if($job->scheduler_id == $this->scheduler->id) {
253 $this->assertNotEmpty($ourjob, "Could not find our job in the queue");
255 $queue = new MockSchedulerQueue();
256 $this->scheduler->checkPendingJobs($queue);
258 foreach($queue->jobs as $job) {
259 if($job->scheduler_id == $this->scheduler->id) {
264 $this->assertEmpty($ourjob2, "Copy job submitted");
265 // set job to running
266 $ourjob->status = SchedulersJob::JOB_STATUS_RUNNING;
268 $queue = new MockSchedulerQueue();
269 $this->scheduler->checkPendingJobs($queue);
271 foreach($queue->jobs as $job) {
272 if($job->scheduler_id == $this->scheduler->id) {
277 $this->assertEmpty($ourjob2, "Copy job submitted");
279 $ourjob->status = SchedulersJob::JOB_STATUS_DONE;
281 $queue = new MockSchedulerQueue();
282 $this->scheduler->checkPendingJobs($queue);
284 foreach($queue->jobs as $job) {
285 if($job->scheduler_id == $this->scheduler->id) {
290 $this->assertNotEmpty($ourjob, "Could not find our job in the queue");
293 public function testJobsCleanupReschedule()
295 $this->scheduler->job_interval = "*::*::*::*::*";
296 $this->scheduler->job = "test::test";
297 $this->scheduler->status = "Active";
298 $this->scheduler->new_with_id = true;
299 $this->scheduler->save();
301 $job = new SchedulersJob();
302 $job->update_date_modified = false;
303 $job->status = SchedulersJob::JOB_STATUS_RUNNING;
304 $job->scheduler_id = $this->scheduler->id;
305 $job->execute_time = $GLOBALS['timedate']->nowDb();
306 $job->date_entered = '2010-01-01 12:00:00';
307 $job->date_modified = '2010-01-01 12:00:00';
308 $job->name = "Unit test Job 1";
309 $job->target = "test::test";
310 $job->assigned_user_id = $GLOBALS['current_user']->id;
313 // try queue run with old job stuck
314 $queue = new MockSchedulerQueue();
315 $this->scheduler->checkPendingJobs($queue);
317 foreach($queue->jobs as $job) {
318 if($job->scheduler_id == $this->scheduler->id) {
323 $this->assertEmpty($ourjob, "Duplicate job found");
324 // now cleanup the job
326 $job = new SchedulersJob();
327 $job->retrieve($jobid);
328 $this->assertEquals(SchedulersJob::JOB_STATUS_DONE, $job->status, "Wrong status");
329 $this->assertEquals(SchedulersJob::JOB_FAILURE, $job->resolution, "Wrong resolution");
330 // now try again - should schedule now
331 $queue = new MockSchedulerQueue();
332 $this->scheduler->checkPendingJobs($queue);
334 foreach($queue->jobs as $job) {
335 if($job->scheduler_id == $this->scheduler->id) {
340 $this->assertNotEmpty($ourjob, "Could not find our job in the queue");
345 class MockSchedulerQueue extends SugarJobQueue
347 public $jobs = array();
349 public function submitJob($job)
351 $this->jobs[] = $job;
352 parent::submitJob($job);
356 class TestScheduler extends Scheduler
358 public $fired = false;
360 public $name = "testJob";
361 public $date_time_start = '2005-01-01 19:00:00';
363 public function fire() {