View Javadoc
1   /*
2    * Copyright (C) 2009 Jayway AB
3    * Copyright (C) 2007-2008 JVending Masa
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package com.simpligility.maven.plugins.android.standalonemojos;
18  
19  import java.io.IOException;
20  
21  import org.apache.maven.plugin.MojoExecutionException;
22  import org.apache.maven.plugin.MojoFailureException;
23  import org.apache.maven.plugins.annotations.Mojo;
24  import org.apache.maven.plugins.annotations.Parameter;
25  
26  import com.android.ddmlib.AdbCommandRejectedException;
27  import com.android.ddmlib.IDevice;
28  import com.android.ddmlib.ShellCommandUnresponsiveException;
29  import com.android.ddmlib.TimeoutException;
30  import com.android.ddmlib.testrunner.MonkeyTestRunner;
31  import com.simpligility.maven.plugins.android.AbstractAndroidMojo;
32  import com.simpligility.maven.plugins.android.AndroidTestRunListener;
33  import com.simpligility.maven.plugins.android.DeviceCallback;
34  import com.simpligility.maven.plugins.android.common.DeviceHelper;
35  import com.simpligility.maven.plugins.android.config.ConfigHandler;
36  import com.simpligility.maven.plugins.android.config.ConfigPojo;
37  import com.simpligility.maven.plugins.android.config.PullParameter;
38  import com.simpligility.maven.plugins.android.configuration.Monkey;
39  
40  /**
41   * Can execute tests using UI/Application Exerciser Monkey.<br>
42   * Implements parsing parameters from pom or command line arguments and sets useful defaults as well. This goal will
43   * invoke Android Monkey exerciser. If the application crashes during the exercise, this goal can fail the build. <br>
44   * A typical usage of this goal can be found at <a
45   * href="https://github.com/stephanenicolas/Quality-Tools-for-Android">Quality tools for Android project</a>.
46   * 
47   * @see <a href="http://developer.android.com/tools/help/monkey.html">Monkey docs by Google</a>
48   * @see <a href="http://stackoverflow.com/q/3968064/693752">Stack Over Flow thread for parsing monkey output.</a>
49   * @author Stéphane Nicolas - snicolas@octo.com
50   */
51  @SuppressWarnings( "unused" )
52  @Mojo( name = "monkey" )
53  public class MonkeyMojo extends AbstractAndroidMojo
54  {
55      /**
56       * -Dmaven.test.skip is commonly used with Maven to skip tests. We honor it.
57       */
58      @Parameter( property = "maven.test.skip", defaultValue = "false", readonly = true )
59      private boolean mavenTestSkip;
60  
61      /**
62       * -DskipTests is commonly used with Maven to skip tests. We honor it too.
63       */
64      @Parameter( property = "skipTests", defaultValue = "false", readonly = true )
65      private boolean mavenSkipTests;
66  
67      /**
68       * -Dmaven.test.failure.ignore is commonly used with Maven to prevent failure of build when (some) tests fail. We
69       * honor it too.
70       */
71      @Parameter( property = "maven.test.failure.ignore", defaultValue = "false", readonly = true )
72      private boolean mavenTestFailureIgnore;
73  
74      /**
75       * -Dmaven.test.failure.ignore is commonly used with Maven to prevent failure of build when (some) tests fail. We
76       * honor it too.
77       */
78      @Parameter( property = "testFailureIgnore", defaultValue = "false", readonly = true )
79      private boolean mavenIgnoreTestFailure;
80  
81      /**
82       * The configuration for the ui automator goal. As soon as a lint goal is invoked the command will be executed
83       * unless the skip parameter is set. A minimal configuration that will run lint and produce a XML report in
84       * ${project.build.directory}/lint/lint-results.xml is
85       * 
86       * <pre>
87       * &lt;monkey&gt;
88       *   &lt;skip&gt;false&lt;/skip&gt;
89       * &lt;/monkey&gt;
90       * </pre>
91       * 
92       * Full configuration can use these parameters.
93       * 
94       * <pre>
95       *  &lt;monkey&gt;
96       *    &lt;skip&gt;false&lt;/skip&gt;
97       *    &lt;eventCount&gt;5000&lt;/eventCount&gt;
98       *    &lt;seed&gt;123456&lt;/seed&gt;
99       *    &lt;throttle&gt;10&lt;/throttle&gt;
100      *    &lt;percentTouch&gt;10&lt;/percentTouch&gt;
101      *    &lt;percentMotion&gt;10&lt;/percentMotion&gt;
102      *    &lt;percentTrackball&gt;10&lt;/percentTrackball&gt;
103      *    &lt;percentNav&gt;10&lt;/percentNav&gt;
104      *    &lt;percentMajorNav&gt;10&lt;/percentMajorNav&gt;
105      *    &lt;percentSyskeys&gt;10&lt;/percentSyskeys&gt;
106      *    &lt;percentAppswitch&gt;10&lt;/percentAppswitch&gt;
107      *    &lt;percentAnyevent&gt;10&lt;/percentAnyevent&gt;
108      *    &lt;packages&gt;
109      *        &lt;package&gt;com.foo&lt;/package&gt;
110      *        &lt;package&gt;com.bar&lt;/package&gt;
111      *    &lt;/packages&gt;
112      *    &lt;categories&gt;
113      *        &lt;category&gt;foo&lt;/category&gt;
114      *        &lt;category&gt;bar&lt;/category&gt;
115      *    &lt;/categories&gt;
116      *    &lt;debugNoEvents&gt;true&lt;/debugNoEvents&gt;
117      *    &lt;hprof&gt;true&lt;/hprof&gt;
118      *    &lt;ignoreCrashes&gt;true&lt;/ignoreCrashes&gt;
119      *    &lt;ignoreTimeouts&gt;true&lt;/ignoreTimeouts&gt;
120      *    &lt;ignoreSecurityExceptions&gt;true&lt;/ignoreSecurityExceptions&gt;
121      *    &lt;killProcessAfterError&gt;true&lt;/killProcessAfterError&gt;
122      *    &lt;monitorNativeCrashes&gt;true&lt;/monitorNativeCrashes&gt;
123      *    &lt;createReport&gt;true&lt;/createReport&gt;
124      *  &lt;/monkey&gt;
125      * </pre>
126      * 
127      * Alternatively to the plugin configuration values can also be configured as properties on the command line as
128      * android.lint.* or in pom or settings file as properties like lint*.
129      */
130     @Parameter
131     @ConfigPojo
132     private Monkey monkey;
133 
134     /**
135      * Enables or disables monkey test goal. If <code>true</code> it will be skipped; if <code>false</code>, it will be
136      * run. Defaults to true.
137      */
138     @Parameter( property = "android.monkey.skip" )
139     private Boolean monkeySkip;
140 
141     @PullParameter( defaultValue = "true" )
142     private Boolean parsedSkip;
143 
144     /**
145      * Number of generated events. Defaults to 1000.
146      */
147     @Parameter( property = "android.monkey.eventCount" )
148     private Integer monkeyEventCount;
149 
150     @PullParameter( required = true, defaultValue = "1000" )
151     private Integer parsedEventCount;
152 
153     /**
154      * Seed value for pseudo-random number generator. If you re-run the Monkey with the same seed value, it will
155      * generate the same sequence of events.
156      */
157     @Parameter( property = "android.monkey.seed" )
158     private Long monkeySeed;
159 
160     @PullParameter( required = false, defaultValueGetterMethod = "getSeed" )
161     private Long parsedSeed;
162 
163     /**
164      * Inserts a fixed delay between events. You can use this option to slow down the Monkey. If not specified, there is
165      * no delay and the events are generated as rapidly as possible.
166      */
167     @Parameter( property = "android.monkey.throttle" )
168     private Long monkeyThrottle;
169 
170     @PullParameter( required = false, defaultValueGetterMethod = "getThrottle" )
171     private Long parsedThrottle;
172 
173     /**
174      * Adjust percentage of touch events. (Touch events are a down-up event in a single place on the screen.)
175      */
176     @Parameter( property = "android.monkey.percentTouch" )
177     private Integer monkeyPercentTouch;
178 
179     @PullParameter( required = false, defaultValueGetterMethod = "getPercentTouch" )
180     private Integer parsedPercentTouch;
181 
182     /**
183      * Adjust percentage of motion events. (Motion events consist of a down event somewhere on the screen, a series of
184      * pseudo-random movements, and an up event.)
185      */
186     @Parameter( property = "android.monkey.percentMotion" )
187     private Integer monkeyPercentMotion;
188 
189     @PullParameter( required = false, defaultValueGetterMethod = "getPercentMotion" )
190     private Integer parsedPercentMotion;
191 
192     /**
193      * Adjust percentage of trackball events. (Trackball events consist of one or more random movements, sometimes
194      * followed by a click.)
195      */
196     @Parameter( property = "android.monkey.percentTrackball" )
197     private Integer monkeyPercentTrackball;
198 
199     @PullParameter( required = false, defaultValueGetterMethod = "getPercentTrackball" )
200     private Integer parsedPercentTrackball;
201 
202     /**
203      * Adjust percentage of "basic" navigation events. (Navigation events consist of up/down/left/right, as input from a
204      * directional input device.)
205      */
206     @Parameter( property = "android.monkey.percentNav" )
207     private Integer monkeyPercentNav;
208 
209     @PullParameter( required = false, defaultValueGetterMethod = "getPercentNav" )
210     private Integer parsedPercentNav;
211 
212     /**
213      * Adjust percentage of "major" navigation events. (These are navigation events that will typically cause actions
214      * within your UI, such as the center button in a 5-way pad, the back key, or the menu key.)
215      */
216     @Parameter( property = "android.monkey.percentMajorNav" )
217     private Integer monkeyPercentMajorNav;
218 
219     @PullParameter( required = false, defaultValueGetterMethod = "getPercentMajorNav" )
220     private Integer parsedPercentMajorNav;
221 
222     /**
223      * Adjust percentage of "system" key events. (These are keys that are generally reserved for use by the system, such
224      * as Home, Back, Start Call, End Call, or Volume controls.) Defaults to null.
225      */
226     @Parameter( property = "android.monkey.percentSyskeys" )
227     private Integer monkeyPercentSyskeys;
228 
229     @PullParameter( required = false, defaultValueGetterMethod = "getPercentSyskeys" )
230     private Integer parsedPercentSyskeys;
231 
232     /**
233      * Adjust percentage of activity launches. At random intervals, the Monkey will issue a startActivity() call, as a
234      * way of maximizing coverage of all activities within your package.
235      */
236     @Parameter( property = "android.monkey.percentAppswitch" )
237     private Integer monkeyPercentAppswitch;
238 
239     @PullParameter( required = false, defaultValueGetterMethod = "getPercentAppswitch" )
240     private Integer parsedPercentAppswitch;
241 
242     /**
243      * Adjust percentage of other types of events. This is a catch-all for all other types of events such as keypresses,
244      * other less-used buttons on the device, and so forth.
245      */
246     @Parameter( property = "android.monkey.percentAnyevent" )
247     private Integer monkeyPercentAnyEvent;
248 
249     @PullParameter( required = false, defaultValueGetterMethod = "getPercentAnyevent" )
250     private Integer parsedPercentAnyevent;
251 
252     /**
253      * If you specify one or more packages this way, the Monkey will only allow the system to visit activities within
254      * those packages. If your application requires access to activities in other packages (e.g. to select a contact)
255      * you'll need to specify those packages as well. If you don't specify any packages, the Monkey will allow the
256      * system to launch activities in all packages.
257      */
258     @Parameter( property = "android.monkey.packages" )
259     private String[] monkeyPackages;
260 
261     @PullParameter( required = false, defaultValueGetterMethod = "getPackages" )
262     private String[] parsedPackages;
263 
264     /**
265      * If you specify one or more categories this way, the Monkey will only allow the system to visit activities that
266      * are listed with one of the specified categories. If you don't specify any categories, the Monkey will select
267      * activities listed with the category Intent.CATEGORY_LAUNCHER or Intent.CATEGORY_MONKEY.
268      */
269     @Parameter( property = "android.monkey.categories" )
270     private String[] monkeyCategories;
271 
272     @PullParameter( required = false, defaultValueGetterMethod = "getCategories" )
273     private String[] parsedCategories;
274 
275     /**
276      * When specified, the Monkey will perform the initial launch into a test activity, but will not generate any
277      * further events. For best results, combine with -v, one or more package constraints, and a non-zero throttle to
278      * keep the Monkey running for 30 seconds or more. This provides an environment in which you can monitor package
279      * transitions invoked by your application.
280      */
281     @Parameter( property = "android.monkey.debugNoEvents" )
282     private Boolean monkeyDebugNoEvents;
283 
284     @PullParameter( defaultValue = "false" )
285     private Boolean parsedDebugNoEvents;
286 
287     /**
288      * If set, this option will generate profiling reports immediately before and after the Monkey event sequence. This
289      * will generate large (~5Mb) files in data/misc, so use with care. See Traceview for more information on trace
290      * files.
291      */
292     @Parameter( property = "android.monkey.Hprof" )
293     private Boolean monkeyHprof;
294 
295     @PullParameter( defaultValue = "false" )
296     private Boolean parsedHprof;
297 
298     /**
299      * Normally, the Monkey will stop when the application crashes or experiences any type of unhandled exception. If
300      * you specify this option, the Monkey will continue to send events to the system, until the count is completed.
301      * Settings this option is different to setting testFailureIgnore or maven.test.failure.ignore to true, it will
302      * impact monkey run but not the result of the maven build.
303      */
304     @Parameter( property = "android.monkey.ignoreCrashes" )
305     private Boolean monkeyIgnoreCrashes;
306 
307     @PullParameter( defaultValue = "false" )
308     private Boolean parsedIgnoreCrashes;
309 
310     /**
311      * Normally, the Monkey will stop when the application experiences any type of timeout error such as a
312      * "Application Not Responding" dialog. If you specify this option, the Monkey will continue to send events to the
313      * system, until the count is completed.
314      *
315      * Defaults to false.
316      */
317     @Parameter( property = "android.monkey.IgnoreTimeouts" )
318     private Boolean monkeyIgnoreTimeouts;
319 
320     @PullParameter( defaultValue = "false" )
321     private Boolean parsedIgnoreTimeouts;
322 
323     /**
324      * Normally, the Monkey will stop when the application experiences any type of permissions error, for example if it
325      * attempts to launch an activity that requires certain permissions. If you specify this option, the Monkey will
326      * continue to send events to the system, until the count is completed. *
327      * 
328      * Defaults to false.
329      */
330     @Parameter( property = "android.monkey.IgnoreSecurityExceptions" )
331     private Boolean monkeyIgnoreSecurityExceptions;
332 
333     @PullParameter( defaultValue = "false" )
334     private Boolean parsedIgnoreSecurityExceptions;
335 
336     /**
337      * Normally, when the Monkey stops due to an error, the application that failed will be left running. When this
338      * option is set, it will signal the system to stop the process in which the error occurred. Note, under a normal
339      * (successful) completion, the launched process(es) are not stopped, and the device is simply left in the last
340      * state after the final event.
341      * 
342      * Defaults to false.
343      */
344     @Parameter( property = "android.monkey.KillProcessAfterError" )
345     private Boolean monkeyKillProcessAfterError;
346 
347     @PullParameter( defaultValue = "false" )
348     private Boolean parsedKillProcessAfterError;
349 
350     /**
351      * Watches for and reports crashes occurring in the Android system native code. If --kill-process-after-error is
352      * set, the system will stop.
353      * 
354      * Defaults to false.
355      */
356     @Parameter( property = "android.monkey.MonitorNativeCrashes" )
357     private Boolean monkeyMonitorNativeCrashes;
358 
359     @PullParameter( defaultValue = "false" )
360     private Boolean parsedMonitorNativeCrashes;
361 
362     /**
363      * Create a junit xml format compatible output file containing the test results for each device the instrumentation
364      * tests run on. <br>
365      * <br>
366      * The files are stored in target/surefire-reports and named TEST-deviceid.xml. The deviceid for an emulator is
367      * deviceSerialNumber_avdName_manufacturer_model. The serial number is commonly emulator-5554 for the first emulator
368      * started with numbers increasing. avdName is as defined in the SDK tool. The manufacturer is typically "unknown"
369      * and the model is typically "sdk".<br>
370      * The deviceid for an actual devices is deviceSerialNumber_manufacturer_model. <br>
371      * <br>
372      * The file contains system properties from the system running the Android Maven Plugin (JVM) and device properties
373      * from the device/emulator the tests are running on. <br>
374      * <br>
375      * The file contains a single TestSuite for all tests and a TestCase for each test method. Errors and failures are
376      * logged in the file and the system log with full stack traces and other details available.
377      * 
378      * Defaults to false.
379      */
380     @Parameter( property = "android.monkey.createReport" )
381     private Boolean monkeyCreateReport;
382 
383     @PullParameter( defaultValue = "false" )
384     private Boolean parsedCreateReport;
385 
386     @Override
387     public void execute() throws MojoExecutionException, MojoFailureException
388     {
389         ConfigHandler configHandler = new ConfigHandler( this, this.session, this.execution );
390         configHandler.parseConfiguration();
391 
392         if ( isEnableIntegrationTest() )
393         {
394             exerciseApp();
395         }
396     }
397 
398     /**
399      * Whether or not tests are enabled.
400      * 
401      * @return a boolean indicating whether or not tests are enabled.
402      */
403     protected boolean isEnableIntegrationTest()
404     {
405         return !parsedSkip && !mavenTestSkip && !mavenSkipTests;
406     }
407 
408     /**
409      * Whether or not test failures should be ignored.
410      * 
411      * @return a boolean indicating whether or not test failures should be ignored.
412      */
413     protected boolean isIgnoreTestFailures()
414     {
415         return mavenIgnoreTestFailure || mavenTestFailureIgnore;
416     }
417 
418     /**
419      * Actually plays tests.
420      * 
421      * @throws MojoExecutionException
422      *             if exercising app threw an exception and isIgnoreTestFailures is false..
423      * @throws MojoFailureException
424      *             if exercising app failed and isIgnoreTestFailures is false.
425      */
426     protected void exerciseApp() throws MojoExecutionException, MojoFailureException
427     {
428 
429         getLog().debug( "Parsed values for Android Monkey invocation: " );
430         getLog().debug( "seed:" + parsedSeed );
431 
432         DeviceCallback instrumentationTestExecutor = new DeviceCallback()
433         {
434             @Override
435             public void doWithDevice( final IDevice device ) throws MojoExecutionException, MojoFailureException
436             {
437                 String deviceLogLinePrefix = DeviceHelper.getDeviceLogLinePrefix( device );
438 
439                 MonkeyTestRunner monkeyTestRunner = new MonkeyTestRunner( parsedEventCount, device );
440 
441                 monkeyTestRunner.setRunName( "ui monkey tests" );
442                 if ( parsedSeed != null )
443                 {
444                     monkeyTestRunner.setSeed( parsedSeed );
445                 }
446                 if ( parsedPercentTouch != null )
447                 {
448                     monkeyTestRunner.setPercentTouch( parsedPercentTouch );
449                 }
450                 if ( parsedPercentMotion != null )
451                 {
452                     monkeyTestRunner.setPercentTouch( parsedPercentMotion );
453                 }
454                 if ( parsedPercentTrackball != null )
455                 {
456                     monkeyTestRunner.setPercentTrackball( parsedPercentTrackball );
457                 }
458                 if ( parsedPercentNav != null )
459                 {
460                     monkeyTestRunner.setPercentNav( parsedPercentNav );
461                 }
462                 if ( parsedPercentMajorNav != null )
463                 {
464                     monkeyTestRunner.setPercentMajorNav( parsedPercentMajorNav );
465                 }
466                 if ( parsedPercentSyskeys != null )
467                 {
468                     monkeyTestRunner.setPercentSyskeys( parsedPercentSyskeys );
469                 }
470                 if ( parsedPercentAppswitch != null )
471                 {
472                     monkeyTestRunner.setPercentAppswitch( parsedPercentAppswitch );
473                 }
474                 if ( parsedPercentAnyevent != null )
475                 {
476                     monkeyTestRunner.setPercentAnyEvent( parsedPercentAnyevent );
477                 }
478                 if ( parsedPackages != null )
479                 {
480                     monkeyTestRunner.setPackages( parsedPackages );
481                 }
482                 if ( parsedCategories != null )
483                 {
484                     monkeyTestRunner.setCategories( parsedCategories );
485                 }
486                 monkeyTestRunner.setDebugNoEvents( parsedDebugNoEvents );
487                 monkeyTestRunner.setHprof( parsedHprof );
488                 monkeyTestRunner.setIgnoreCrashes( parsedIgnoreCrashes );
489                 monkeyTestRunner.setIgnoreTimeouts( parsedIgnoreTimeouts );
490                 monkeyTestRunner.setIgnoreSecurityExceptions( parsedIgnoreSecurityExceptions );
491                 monkeyTestRunner.setKillProcessAfterError( parsedKillProcessAfterError );
492                 monkeyTestRunner.setMonitorNativeCrash( parsedMonitorNativeCrashes );
493 
494                 getLog().info( deviceLogLinePrefix + "Running ui monkey tests" );
495                 try
496                 {
497                     AndroidTestRunListener testRunListener = new AndroidTestRunListener( device, getLog(),
498                             parsedCreateReport, false, "", "", targetDirectory );
499                     monkeyTestRunner.run( testRunListener );
500                     if ( testRunListener.hasFailuresOrErrors() && !isIgnoreTestFailures() )
501                     {
502                         throw new MojoFailureException( deviceLogLinePrefix + "Tests failed on device." );
503                     }
504                     if ( testRunListener.testRunFailed() )
505                     {
506                         throw new MojoFailureException( deviceLogLinePrefix + "Test run failed to complete: "
507                                 + testRunListener.getTestRunFailureCause() );
508                     }
509                     if ( testRunListener.threwException() && !isIgnoreTestFailures() )
510                     {
511                         throw new MojoFailureException( deviceLogLinePrefix + testRunListener.getExceptionMessages() );
512                     }
513                 }
514                 catch ( TimeoutException e )
515                 {
516                     throw new MojoExecutionException( deviceLogLinePrefix + "timeout", e );
517                 }
518                 catch ( AdbCommandRejectedException e )
519                 {
520                     throw new MojoExecutionException( deviceLogLinePrefix + "adb command rejected", e );
521                 }
522                 catch ( ShellCommandUnresponsiveException e )
523                 {
524                     throw new MojoExecutionException( deviceLogLinePrefix + "shell command " + "unresponsive", e );
525                 }
526                 catch ( IOException e )
527                 {
528                     throw new MojoExecutionException( deviceLogLinePrefix + "IO problem", e );
529                 }
530             }
531         };
532 
533         doWithDevices( instrumentationTestExecutor );
534     }
535 
536     /**
537      * @return default seed.
538      */
539     // used via PullParameter annotation - do not remove
540     private Long getSeed()
541     {
542         return parsedSeed;
543     }
544 
545     /**
546      * @return default throttle.
547      */
548     // used via PullParameter annotation - do not remove
549     private Long getThrottle()
550     {
551         return parsedThrottle;
552     }
553 
554     /**
555      * @return default percentTouch.
556      */
557     // used via PullParameter annotation - do not remove
558     private Integer getPercentTouch()
559     {
560         return parsedPercentTouch;
561     }
562 
563     /**
564      * @return default percentMotion.
565      */
566     // used via PullParameter annotation - do not remove
567     private Integer getPercentMotion()
568     {
569         return parsedPercentMotion;
570     }
571 
572     /**
573      * @return default percentTrackball.
574      */
575     // used via PullParameter annotation - do not remove
576     private Integer getPercentTrackball()
577     {
578         return parsedPercentTrackball;
579     }
580 
581     /**
582      * @return default percentNav.
583      */
584     // used via PullParameter annotation - do not remove
585     private Integer getPercentNav()
586     {
587         return parsedPercentNav;
588     }
589 
590     /**
591      * @return default percentMajorNav.
592      */
593     // used via PullParameter annotation - do not remove
594     private Integer getPercentMajorNav()
595     {
596         return parsedPercentMajorNav;
597     }
598 
599     /**
600      * @return default percentSyskeys.
601      */
602     // used via PullParameter annotation - do not remove
603     private Integer getPercentSyskeys()
604     {
605         return parsedPercentSyskeys;
606     }
607 
608     /**
609      * @return default percentAppSwitch.
610      */
611     // used via PullParameter annotation - do not remove
612     private Integer getPercentAppswitch()
613     {
614         return parsedPercentAppswitch;
615     }
616 
617     /**
618      * @return default percentAnyEvent.
619      */
620     // used via PullParameter annotation - do not remove
621     private Integer getPercentAnyevent()
622     {
623         return parsedPercentAnyevent;
624     }
625 
626     /**
627      * @return default packages.
628      */
629     public String[] getPackages()
630     {
631         return parsedPackages;
632     }
633 
634     /**
635      * @return default categories.
636      */
637     public String[] getCategories()
638     {
639         return parsedCategories;
640     }
641 }