<?xml version="1.0" encoding="UTF-8"?>

<!--
    Copyright (c) 2011-2017 Apple Inc. All rights reserved.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
  -->

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<!-- Define the kinds of software and user behavior the load simulation
			will simulate. -->
		<key>clients</key>

		<!-- Have as many different kinds of software and user behavior configurations
			as you want. Each is a dict -->
		<array>

			<dict>

				<!-- Here is a OS X client simulator. -->
				<key>software</key>
				<string>contrib.performance.loadtest.ical.OS_X_10_11</string>

				<!-- Arguments to use to initialize the OS_X_10_11 instance. -->
				<key>params</key>
				<dict>
					<!-- Name that appears in logs. -->
					<key>title</key>
					<string>main</string>

					<!-- OS_X_10_11 can poll the calendar home at some interval. This is
						in seconds. -->
					<key>calendarHomePollInterval</key>
					<integer>30</integer>

					<!-- If the server advertises xmpp push, OS_X_10_7 can wait for notifications
						about calendar home changes instead of polling for them periodically. If
						this option is true, then look for the server advertisement for xmpp push
						and use it if possible. Still fall back to polling if there is no xmpp push
						advertised. -->
					<key>supportPush</key>
					<true/>

					<key>supportAmpPush</key>
					<true/>

					<key>unauthenticatedPercentage</key>
					<integer>40</integer>

				</dict>

				<!-- The profiles define certain types of user behavior on top of the
					client software being simulated. -->
				<key>profiles</key>
				<array>

					<!-- First an event-creating profile, which will periodically create
						new events at a random time on a random calendar. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.Eventer</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to create a new event. -->
							<key>interval</key>
							<integer>120</integer>

							<!-- Define how start times (DTSTART) for the randomly generated events
								will be selected. This is an example of a "Distribution" parameter. The value
								for most "Distribution" parameters are interchangeable and extensible. -->
							<key>eventStartDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized. It produces timestamps
									in the near future, limited to certain days of the week and certain hours
									of the day. -->
								<key>type</key>
								<string>contrib.performance.stats.WorkDistribution</string>

								<key>params</key>
								<dict>
									<!-- These are the days of the week the distribution will use. -->
									<key>daysOfWeek</key>
									<array>
										<string>mon</string>
										<string>tue</string>
										<string>wed</string>
										<string>thu</string>
										<string>fri</string>
									</array>

									<!-- The earliest hour of a day at which an event might be scheduled. -->
									<key>beginHour</key>
									<integer>8</integer>

									<!-- And the latest hour of a day (at which an event will be scheduled
										to begin!). -->
									<key>endHour</key>
									<integer>16</integer>

									<!-- The timezone in which the event is scheduled. (XXX Does this
										really work right?) -->
									<key>tzname</key>
									<string>America/Los_Angeles</string>
								</dict>
							</dict>

							<!-- Define how recurrences are created. -->
							<key>recurrenceDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized.  We have a fixed set of
								     RRULEs defined for this distribution and pick each based on a
								     weight. -->
								<key>type</key>
								<string>contrib.performance.stats.RecurrenceDistribution</string>

								<key>params</key>
								<dict>
									<!-- False to disable RRULEs -->
									<key>allowRecurrence</key>
									<true/>

									<!-- These are the weights for the specific set of RRULEs. -->
									<key>weights</key>
									<dict>
										<!-- Half of all events will be non-recurring -->
										<key>none</key>
										<integer>50</integer>

										<!-- Daily and weekly are pretty common -->
										<key>daily</key>
										<integer>10</integer>
										<key>weekly</key>
										<integer>20</integer>

										<!-- Monthly, yearly, daily & weekly limit not so common -->
										<key>monthly</key>
										<integer>2</integer>
										<key>yearly</key>
										<integer>1</integer>
										<key>dailylimit</key>
										<integer>2</integer>
										<key>weeklylimit</key>
										<integer>5</integer>

										<!-- Work days pretty common -->
										<key>workdays</key>
										<integer>10</integer>
									</dict>
								</dict>
							</dict>

							<!-- Percentage of time to attach a file -->
							<key>fileAttachPercentage</key>
							<integer>1</integer>

							<!-- Define the attachment size distribution. -->
							<key>fileSizeDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.NormalDistribution</string>
								<key>params</key>
								<dict>
									<!-- mu gives the mean of the normal distribution (in bytes). -->
									<key>mu</key>
									<integer>500000</integer>

									<!-- and sigma gives its standard deviation. -->
									<key>sigma</key>
									<integer>100000</integer>
								</dict>
							</dict>

						</dict>
					</dict>

					<!-- This profile will create a new event, and then periodically update the ACKNOWLEDGED property. -->

					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.AlarmAcknowledger</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<false/>

							<!-- Define how often to check (in seconds) whether it's
							time to do an alarm acknowledgement -->
							<key>interval</key>
							<integer>15</integer>

							<!-- Acknowledgements will only actually occur at one of these
							minutes past the hour: -->
							<key>pastTheHour</key>
							<array>
								<integer>0</integer>
								<integer>15</integer>
								<integer>30</integer>
								<integer>45</integer>
							</array>

							<!-- Define how start times (DTSTART) for the randomly generated events
								will be selected. This is an example of a "Distribution" parameter. The value
								for most "Distribution" parameters are interchangeable and extensible. -->
							<key>eventStartDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized. It produces timestamps
									in the near future, limited to certain days of the week and certain hours
									of the day. -->
								<key>type</key>
								<string>contrib.performance.stats.WorkDistribution</string>

								<key>params</key>
								<dict>
									<!-- These are the days of the week the distribution will use. -->
									<key>daysOfWeek</key>
									<array>
										<string>mon</string>
										<string>tue</string>
										<string>wed</string>
										<string>thu</string>
										<string>fri</string>
									</array>

									<!-- The earliest hour of a day at which an event might be scheduled. -->
									<key>beginHour</key>
									<integer>8</integer>

									<!-- And the latest hour of a day (at which an event will be scheduled
										to begin!). -->
									<key>endHour</key>
									<integer>16</integer>

									<!-- The timezone in which the event is scheduled. (XXX Does this
										really work right?) -->
									<key>tzname</key>
									<string>America/Los_Angeles</string>
								</dict>
							</dict>

							<!-- Define how recurrences are created. -->
							<key>recurrenceDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized.  We have a fixed set of
								     RRULEs defined for this distribution and pick each based on a
								     weight. -->
								<key>type</key>
								<string>contrib.performance.stats.RecurrenceDistribution</string>

								<key>params</key>
								<dict>
									<!-- False to disable RRULEs -->
									<key>allowRecurrence</key>
									<true/>

									<!-- These are the weights for the specific set of RRULEs. -->
									<key>weights</key>
									<dict>
										<!-- Half of all events will be non-recurring -->
										<key>none</key>
										<integer>50</integer>

										<!-- Daily and weekly are pretty common -->
										<key>daily</key>
										<integer>25</integer>
										<key>weekly</key>
										<integer>25</integer>

										<!-- Monthly, yearly, daily & weekly limit not so common -->
										<key>monthly</key>
										<integer>0</integer>
										<key>yearly</key>
										<integer>0</integer>
										<key>dailylimit</key>
										<integer>0</integer>
										<key>weeklylimit</key>
										<integer>0</integer>

										<!-- Work days pretty common -->
										<key>workdays</key>
										<integer>0</integer>
									</dict>
								</dict>
							</dict>
						</dict>
					</dict>

					<!-- Picks a random event and changes the title -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.TitleChanger</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to create a new event. -->
							<key>interval</key>
							<integer>120</integer>

						</dict>
					</dict>

					<!-- Picks a random event and changes the description -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.DescriptionChanger</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to create a new event. -->
							<key>interval</key>
							<integer>120</integer>

							<!-- Define the description length distribution. -->
							<key>descriptionLengthDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.LogNormalDistribution</string>
								<key>params</key>
								<dict>
									<!-- mode - peak-->
									<key>mode</key>
									<integer>450</integer>
									<!-- mean - average-->
									<key>median</key>
									<integer>650</integer>
									<!-- maximum -->
									<key>maximum</key>
									<real>1000000</real>
								</dict>
							</dict>

						</dict>
					</dict>


					<!-- Removes events from calendars exceeding a threshold -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.EventCountLimiter</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will check
								for too-large collections. -->
							<key>interval</key>
							<integer>60</integer>

							<!-- The upper bound. -->
							<key>eventCountLimit</key>
							<integer>100</integer>

						</dict>
					</dict>


					<!-- Shares calendars -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.CalendarSharer</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will share calendars. -->
							<key>interval</key>
							<integer>300</integer>

							<!-- Only share a collection with up to this many sharees -->
							<key>maxSharees</key>
							<integer>3</integer>

						</dict>
					</dict>

					<!-- This profile invites some number of new attendees to new events. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.Inviter</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the frequency at which new invitations will be sent out. -->
							<key>sendInvitationDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.NormalDistribution</string>
								<key>params</key>
								<dict>
									<!-- mu gives the mean of the normal distribution (in seconds). -->
									<key>mu</key>
									<integer>120</integer>

									<!-- and sigma gives its standard deviation. -->
									<key>sigma</key>
									<integer>5</integer>
								</dict>
							</dict>

							<!-- Define the distribution of who will be invited to an event.

								When inviteeClumping is turned on each invitee is based on a sample of
								users "close to" the organizer based on account index. If the clumping
								is too "tight" for the requested number of attendees, then invites for
								those larger numbers will simply fail (the sim will report that situation).

								When inviteeClumping is off invitees will be sampled across an entire
								range of account indexes. In this case the distribution ought to be a
								UniformIntegerDistribution with min=0 and max set to the number of accounts.
							-->
							<key>inviteeDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.UniformIntegerDistribution</string>
								<key>params</key>
								<dict>
									<!-- The minimum value (inclusive) of the uniform distribution. -->
									<key>min</key>
									<integer>0</integer>
									<!-- The maximum value (exclusive) of the uniform distribution. -->
									<key>max</key>
									<integer>99</integer>
								</dict>
							</dict>

							<key>inviteeClumping</key>
							<true/>

							<!-- Define the distribution of how many attendees will be invited to an event.

								LogNormal is the best fit to observed data.


								For LogNormal "mode" is the peak, "mean" is the mean value.	For invites,
								mode should typically be 1, and mean whatever matches the user behavior.
								Our typical mean is 6.
							     -->
							<key>inviteeCountDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.LogNormalDistribution</string>
								<key>params</key>
								<dict>
									<!-- mode - peak-->
									<key>mode</key>
									<integer>6</integer>
									<!-- mean - average-->
									<key>median</key>
									<integer>8</integer>
									<!-- maximum -->
									<key>maximum</key>
									<real>60</real>
								</dict>
							</dict>

							<!-- Percentage of time to do a lookup for invitee -->
							<key>inviteeLookupPercentage</key>
							<integer>50</integer>

							<!-- Define how start times (DTSTART) for the randomly generated events
								will be selected. This is an example of a "Distribution" parameter. The value
								for most "Distribution" parameters are interchangeable and extensible. -->
							<key>eventStartDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized. It produces timestamps
									in the near future, limited to certain days of the week and certain hours
									of the day. -->
								<key>type</key>
								<string>contrib.performance.stats.WorkDistribution</string>

								<key>params</key>
								<dict>
									<!-- These are the days of the week the distribution will use. -->
									<key>daysOfWeek</key>
									<array>
										<string>mon</string>
										<string>tue</string>
										<string>wed</string>
										<string>thu</string>
										<string>fri</string>
									</array>

									<!-- The earliest hour of a day at which an event might be scheduled. -->
									<key>beginHour</key>
									<integer>8</integer>

									<!-- And the latest hour of a day (at which an event will be scheduled
										to begin!). -->
									<key>endHour</key>
									<integer>16</integer>

									<!-- The timezone in which the event is scheduled. (XXX Does this
										really work right?) -->
									<key>tzname</key>
									<string>America/Los_Angeles</string>
								</dict>
							</dict>

							<!-- Define how recurrences are created. -->
							<key>recurrenceDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized.  We have a fixed set of
								     RRULEs defined for this distribution and pick each based on a
								     weight. -->
								<key>type</key>
								<string>contrib.performance.stats.RecurrenceDistribution</string>

								<key>params</key>
								<dict>
									<!-- False to disable RRULEs -->
									<key>allowRecurrence</key>
									<true/>

									<!-- These are the weights for the specific set of RRULEs. -->
									<key>weights</key>
									<dict>
										<!-- Half of all events will be non-recurring -->
										<key>none</key>
										<integer>50</integer>

										<!-- Daily and weekly are pretty common -->
										<key>daily</key>
										<integer>10</integer>
										<key>weekly</key>
										<integer>20</integer>

										<!-- Monthly, yearly, daily & weekly limit not so common -->
										<key>monthly</key>
										<integer>2</integer>
										<key>yearly</key>
										<integer>1</integer>
										<key>dailylimit</key>
										<integer>2</integer>
										<key>weeklylimit</key>
										<integer>5</integer>

										<!-- Work days pretty common -->
										<key>workdays</key>
										<integer>10</integer>
									</dict>
								</dict>
							</dict>

							<!-- Percentage of time to attach a file -->
							<key>fileAttachPercentage</key>
							<integer>1</integer>

							<!-- Define the attachment size distribution. -->
							<key>fileSizeDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.NormalDistribution</string>
								<key>params</key>
								<dict>
									<!-- mu gives the mean of the normal distribution (in seconds). -->
									<key>mu</key>
									<integer>500000</integer>

									<!-- and sigma gives its standard deviation. -->
									<key>sigma</key>
									<integer>100000</integer>
								</dict>
							</dict>

						</dict>
					</dict>

					<!-- This profile accepts invitations to events, handles cancels, and
					     handles replies received. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.Accepter</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define how long to wait after seeing a new invitation before
								accepting it.

								For LogNormal "mode" is the peak, "median" is the 50% cummulative value
								(i.e., half of the user have accepted by that time).
							-->
							<key>acceptDelayDistribution</key>
							<dict>
								<key>type</key>
								<string>contrib.performance.stats.LogNormalDistribution</string>
								<key>params</key>
								<dict>
									<!-- mode - peak-->
									<key>mode</key>
									<integer>300</integer>
									<!-- median - 50% done-->
									<key>median</key>
									<integer>1800</integer>
								</dict>
							</dict>
						</dict>
					</dict>


					<!-- This profile downloads attachments when an event changes. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.AttachmentDownloader</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>
						</dict>
					</dict>

					<!-- A task-creating profile, which will periodically create
						new tasks at a random time on a random calendar. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.Tasker</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to create a new task. -->
							<key>interval</key>
							<integer>300</integer>

							<!-- Define how due times (DUE) for the randomly generated tasks
								will be selected. This is an example of a "Distribution" parameter. The value
								for most "Distribution" parameters are interchangeable and extensible. -->
							<key>taskDueDistribution</key>
							<dict>

								<!-- This distribution is pretty specialized. It produces timestamps
									in the near future, limited to certain days of the week and certain hours
									of the day. -->
								<key>type</key>
								<string>contrib.performance.stats.WorkDistribution</string>

								<key>params</key>
								<dict>
									<!-- These are the days of the week the distribution will use. -->
									<key>daysOfWeek</key>
									<array>
										<string>mon</string>
										<string>tue</string>
										<string>wed</string>
										<string>thu</string>
										<string>fri</string>
									</array>

									<!-- The earliest hour of a day at which an event might be scheduled. -->
									<key>beginHour</key>
									<integer>8</integer>

									<!-- And the latest hour of a day (at which an event will be scheduled
										to begin!). -->
									<key>endHour</key>
									<integer>16</integer>

									<!-- The timezone in which the event is scheduled. (XXX Does this
										really work right?) -->
									<key>tzname</key>
									<string>America/Los_Angeles</string>
								</dict>
							</dict>
						</dict>
					</dict>


					<!-- A profile which periodically does calendar-query reports
						on a random calendar. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.TimeRanger</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to run a report. -->
							<key>interval</key>
							<integer>300</integer>

						</dict>
					</dict>


					<!-- A profile which periodically does a "deep refresh" of PROPFINDs
					    on each calendar. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.DeepRefresher</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to refresh. -->
							<key>interval</key>
							<integer>60</integer>

						</dict>
					</dict>

					<!-- A profile which periodically subscribes to APNS. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.APNSSubscriber</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<false/>

							<!-- Define the interval (in seconds) at which this profile will use
								its client to subscribe. -->
							<key>interval</key>
							<integer>60</integer>

						</dict>
					</dict>

					<!-- A profile which will periodically reset itself, as if a user removed
						and re-added their account. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.Resetter</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<false/>

							<!-- Define the interval (in seconds) at which this profile will
								reset its client. -->
							<key>interval</key>
							<integer>600</integer>
						</dict>
					</dict>

				</array>

				<!-- Determine the frequency at which this client configuration will
					appear in the clients which are created by the load tester. -->
				<key>weight</key>
				<integer>1</integer>
			</dict>

			<dict>

				<!-- Here is a more passive OS X client simulator. -->
				<key>software</key>
				<string>contrib.performance.loadtest.ical.OS_X_10_11</string>

				<!-- Arguments to use to initialize the OS_X_10_11 instance. -->
				<key>params</key>
				<dict>
					<!-- Name that appears in logs. -->
					<key>title</key>
					<string>idle</string>

					<!-- OS_X_10_11 can poll the calendar home at some interval. This is
						in seconds. -->
					<key>calendarHomePollInterval</key>
					<integer>30</integer>

					<!-- If the server advertises xmpp push, OS_X_10_7 can wait for notifications
						about calendar home changes instead of polling for them periodically. If
						this option is true, then look for the server advertisement for xmpp push
						and use it if possible. Still fall back to polling if there is no xmpp push
						advertised. -->
					<key>supportPush</key>
					<true/>

					<key>supportAmpPush</key>
					<true/>

					<key>unauthenticatedPercentage</key>
					<integer>40</integer>

				</dict>

				<!-- The profiles define certain types of user behavior on top of the
					client software being simulated. -->
				<key>profiles</key>
				<array>
					<!-- This profile downloads attachments when an event changes. -->
					<dict>
						<key>class</key>
						<string>contrib.performance.loadtest.profiles.AttachmentDownloader</string>

						<key>params</key>
						<dict>
							<key>enabled</key>
							<true/>
						</dict>
					</dict>
				</array>
				<!-- Determine the frequency at which this client configuration will
					appear in the clients which are created by the load tester. -->
				<key>weight</key>
				<integer>1</integer>
			</dict>

		</array>
	</dict>
</plist>
