Completed
Push — 16.1 ( 262232...423caf )
by Nathan
14:23
created

TimezoneTest::tearDownAfterClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Test the recurring event timezone field with different combinations of
5
 * user timezone, server timezone, and event timezone (on both sides of UTC)
6
 *
7
 * @link http://www.egroupware.org
8
 * @author Nathan Gray
9
 * @package calendar
10
 * @copyright (c) 2017  Nathan Gray
11
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
12
 */
13
14
namespace EGroupware\calendar;
15
16
require_once realpath(__DIR__.'/../../api/src/test/AppTest.php');	// Application test base
17
18
use Egroupware\Api;
19
20
class TimezoneTest extends \EGroupware\Api\AppTest {
21
22
	protected $bo;
23
24
	const RECUR_DAYS = 5;
25
26
	protected $recur_end;
27
	protected $cal_id;
28
29
	public static function setUpBeforeClass()
30
	{
31
		parent::setUpBeforeClass();
32
	}
33
	public static function tearDownAfterClass()
34
	{
35
		parent::tearDownAfterClass();
36
	}
37
	
38
	public function setUp()
39
	{
40
		$this->bo = new \calendar_boupdate();
41
42
		//$this->mockTracking($this->bo, 'calendar_tracking');
43
44
		$this->recur_end = new Api\DateTime(mktime(0,0,0,date('m'), date('d') + static::RECUR_DAYS, date('Y')));
45
	}
46
47
	public function tearDown()
48
	{
49
		//$this->bo->delete($this->cal_id);
50
		$this->bo = null;
51
52
		// need to call preferences constructor and read_repository, to set user timezone again
53
		$GLOBALS['egw']->preferences->__construct($GLOBALS['egw_info']['user']['account_id']);
54
		$GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(false);	// no session prefs!
55
56
		// Re-load date/time preferences
57
		Api\DateTime::init();
58
	}
59
60
	/**
61
	 * Test one combination of event / client / server timezone on a daily recurring
62
	 * event to make sure it has the correct number of days, and its timezone
63
	 * stays as set.
64
	 * 
65
	 * @param Array $timezones Timezone settings for event, client & server
66
	 * @param Array $times Start & end hours
67
	 * 
68
	 * @dataProvider eventProvider
69
	 */
70 View Code Duplication
	public function testTimezones($timezones, $times)
71
	{
72
		$this->setTimezones($timezones);
73
74
		$event = $this->makeEvent($timezones, $times);
75
76
		// Save the event
77
		$this->cal_id = $this->bo->save($event);
78
79
		// Check
80
		$this->checkEvent($timezones, $this->cal_id, $times);
81
	}
82
83
	/**
84
	 * Test one combination of event / client / server timezone on a daily recurring
85
	 * all day event to make sure it has the correct number of days, and its timezone
86
	 * stays as set.
87
	 *
88
	 * @param Array $timezones Timezone settings for event, client & server
89
	 * @param Array $times Start & end hours
90
	 *
91
	 * @dataProvider eventProvider
92
	 */
93 View Code Duplication
	public function testTimezonesAllDay($timezones, $times)
94
	{
95
		$this->setTimezones($timezones);
96
97
		$event = $this->makeEvent($timezones, $times, true);
98
99
		// Save the event
100
		$this->cal_id = $this->bo->save($event);
101
102
		// Check
103
		$this->checkEvent($timezones, $this->cal_id, $times);
104
	}
105
106
	/**
107
	 * Load the event and check that it matches expectations
108
	 *
109
	 * @param Array $timezones List of timezones (event, client, server)
110
	 * @param int $cal_id
111
	 * @param Array $times start and end times (just hours)
112
	 */
113
	protected function checkEvent($timezones, $cal_id, $times)
114
	{
115
		// Load the event
116
		// BO does caching, pass ID as array to avoid it
117
		$loaded = $this->bo->read(Array($cal_id));
118
		$loaded = $loaded[$cal_id];
119
120
		$message = $this->makeMessage($timezones, $loaded);
121
122
		$start_time = \mktime($loaded['whole_day'] ? 0 : $times['start'], 0, 0, date('m'), date('d')+1, date('Y'));
123
124
		// Check that the start date is the same (user time)
125
		$this->assertEquals(
126
			Api\DateTime::to($start_time, Api\DateTime::DATABASE),
127
			Api\DateTime::to($loaded['start'], Api\DateTime::DATABASE),
128
			'Start date'. $message
129
		);
130
131
		// Check that the end date is the same (user time)
132
		$this->assertEquals(
133
			Api\DateTime::to(
134
					$loaded['whole_day'] ? \mktime(0, 0, 0, date('m'), date('d')+2, date('Y'))-1 :
135
					\mktime($times['end'], 0, 0, date('m'), date('d')+1, date('Y')
136
			), Api\DateTime::DATABASE),
137
			Api\DateTime::to($loaded['end'], Api\DateTime::DATABASE),
138
			'End date'. $message
139
		);
140
		
141
		// Check event recurring timezone is unchanged
142
		$this->assertEquals($timezones['event'], $loaded['tzid'], 'Timezone' . $message);
143
144
		// Check recurring end date is unchanged (user time)
145
		$loaded_end = new Api\DateTime($loaded['recur_enddate']);
146
		$this->assertEquals($this->recur_end->format('Ymd'), $loaded_end->format('Ymd'), 'Recur end date' . $message);
147
148
		// Recurrences
149
		$so = new \calendar_so();
150
		$recurrences = $so->get_recurrences($cal_id);
151
		unset($recurrences[0]);
152
		$this->assertEquals(static::RECUR_DAYS, count($recurrences), 'Recurrence count' . $message);
153
		foreach($recurrences as $recur_start_time => $participant)
154
		{
155
			$this->assertEquals(
156
					Api\DateTime::to($start_time, 'H:i:s'),
157
					$loaded['whole_day'] ? '00:00:00' : Api\DateTime::to(Api\DateTime::server2user($recur_start_time), 'H:i:s'),
158
					'Recurrence start time' . $message
159
			);
160
		}
161
	}
162
163
	/**
164
	 * Provide an event for checking, along with a list of timezones
165
	 */
166
	public function eventProvider()
167
	{
168
		$tests = array();
169
		$tz_combos = $this->makeTZCombos();
170
171
		// Start times to test, 1 chosen to cross days
172
		$times = array(1, 9);
173
		
174
		foreach($tz_combos as $timezones)
175
		{
176
			foreach($times as $start_time)
177
			{
178
				$tests[] = Array($timezones,
179
					Array(
180
						'start' => $start_time,
181
						'end' => $start_time + 1
182
					)
183
				);
184
			}
185
		}
186
187
		return $tests;
188
	}
189
190
	/**
191
	 * Make a map of all the different client / server / event combinations
192
	 * that we'll use.
193
	 */
194
	protected function makeTZCombos()
195
	{
196
		// Timezone list
197
		$tz_list = Array(
198
			'Pacific/Tahiti',	// -10
199
			'Europe/Berlin',	//  +2
200
			// The first 2 are usually sufficient
201
			//'America/Edmonton',	//  -8
202
			//'Pacific/Auckland',	// +12
203
			//'UTC'
204
		);
205
		$tz_combos = Array();
206
207
		// Pick some timezones to use - every combination from the list
208
		$client_index = $server_index = $event_index = 0;
209
		do {
210
			$tz_combos[] = array(
211
				'client' => $tz_list[$client_index],
212
				'server'	=> $tz_list[$server_index],
213
				'event'		=> $tz_list[$event_index]
214
			);
215
			$client_index++;
216
			if($client_index > count($tz_list)-1)
217
			{
218
				$server_index++;
219
				$client_index = 0;
220
			}
221
			if($server_index > count($tz_list)-1)
222
			{
223
				$event_index++;
224
				$server_index = 0;
225
			}
226
		} while ($event_index < count($tz_list));
227
228
		/* one specific test
229
		$tz_combos = array(array(
230
			'client'	=> 'Europe/Berlin',
231
			'server'	=> 'Pacific/Tahiti',
232
			'event'		=> 'Pacific/Tahiti'
233
		));
234
		// */
235
		return $tz_combos;
236
	}
237
238
	/**
239
	 * Make the array of event information
240
	 *
241
	 * @param Array $timezones
242
	 * @return Array Event array, unsaved.
243
	 * @param boolean $whole_day
244
	 */
245
	protected function makeEvent($timezones, $times, $whole_day = false)
246
	{
247
		$event = array(
248
			'title' => ($whole_day ? 'Whole day ' : '')."Test for " . $this->tzString($timezones),
249
			'des'   => ($whole_day ? 'Whole day ' : '').'Test for test ' . $this->getName() . ' ' . $this->tzString($timezones),
250
			'start' => \mktime($whole_day ? 0 : $times['start'], 0, 0, date('m'), date('d')+1, date('Y')),
251
			'end'   => $whole_day ? \mktime(23, 59, 59, date('m'), date('d')+1, date('Y')) : \mktime($times['end'], 0, 0, date('m'), date('d')+1, date('Y')),
252
			'tzid'	=> $timezones['event'],
253
			'recur_type'	=> 1, // MCAL_RECUR_DAILY
254
			'recur_enddate'	=> $this->recur_end->format('ts'),
255
			'whole_day'		=> $whole_day,
256
			'participants'	=> array(
257
				$GLOBALS['egw_info']['user']['account_id'] => 'A'
258
			)
259
		);
260
		return $event;
261
	}
262
263
	protected function makeMessage($timezones, $event)
264
	{
265
		return ' ' . ($event['id'] ? '[#'.$event['id'] .'] ' : '') . Api\DateTime::to($event['recur_enddate'], Api\DateTime::DATABASE) . ' '.
266
				($event['whole_day'] ? '(whole day) ' : '') . $this->tzString($timezones);
267
	}
268
269
	/**
270
	 * Set the current client & server timezones as given
271
	 * 
272
	 * @param Array $timezones
273
	 */
274
	protected function setTimezones($timezones)
275
	{
276
		// Set the client preference & server preference
277
		$GLOBALS['egw_info']['server']['server_timezone'] = $timezones['server'];
278
		$GLOBALS['egw_info']['user']['preferences']['common']['tz'] = $timezones['client'];
279
280
		// Load date/time preferences into egw_time
281
		Api\DateTime::init();
282
	}
283
284
	/**
285
	 * Make a nice string for the timezone combination we're using
286
	 *
287
	 * @param Array $timezones
288
	 */
289
	protected function tzString($timezones)
290
	{
291
		return "[Event: {$timezones['event']} Client: {$timezones['client']} Server: {$timezones['server']}]";
292
	}
293
}
294