Commit 8e336818 authored by Vitaliy Vashchenko's avatar Vitaliy Vashchenko

Merge branch 'feature-itests' into 'V3.7'

Feature itests

See merge request !733
parents c688eec5 13066d44
......@@ -79,6 +79,8 @@ unitTest:
iTest-desktop:
stage: iTest-desktop
only:
- schedules
script:
- ./gradlew desktop:runITestBundle
artifacts:
......
......@@ -218,6 +218,11 @@ task copyAndroidNatives() {
}
task run(type: Exec) {
def adb = doAndroidSettings()
commandLine "$adb", 'shell', 'am', 'start', '-n', 'cz.nic.tablexia.android/cz.nic.tablexia.android.AndroidLauncher'
}
def doAndroidSettings() {
def path
def localProperties = project.file("../local.properties")
if (localProperties.exists()) {
......@@ -235,8 +240,7 @@ task run(type: Exec) {
path = "$System.env.ANDROID_HOME"
}
def adb = path + "/platform-tools/adb"
commandLine "$adb", 'shell', 'am', 'start', '-n', 'cz.nic.tablexia.android/cz.nic.tablexia.android.AndroidLauncher'
return path + "/platform-tools/adb"
}
// sets up the Android Idea project, using the old Ant based build.
......@@ -261,3 +265,128 @@ idea {
}
}
}
def runTest(String iTestName, String device) {
println "[iTest-" + device + "] STARTING TEST: " + iTestName
def adb = doAndroidSettings()
exec {
commandLine "$adb", '-s', device, 'shell', 'am', 'start', '-n', 'cz.nic.tablexia.itest/cz.nic.tablexia.android.AndroidITestLauncher', '-e', 'testClassName', iTestName
}
def adbStdOut = new ByteArrayOutputStream()
def timeout = 0;
while(timeout<1200) { //timeout is set to 20 minuts
if(adbStdOut.toString().trim().contains("PROCESSING")){
def adbPSOut = new ByteArrayOutputStream()
exec {
commandLine "$adb", '-s', device, 'shell', "ps", "|", "grep", "cz.nic.tablexia.itest"
standardOutput = adbPSOut
}
if(!adbPSOut.toString().contains("cz.nic.tablexia.itest")) return //stop if test is not running anymore
}
if(adbStdOut.toString().trim().contains("OK")) return
if(adbStdOut.toString().trim().contains("FAIL")) return
exec {
commandLine "$adb", '-s', device, 'shell', "ls", "/sdcard/iTest_results"
standardOutput = adbStdOut
}
sleep(1000)
timeout++;
}
println "[iTest-" + device + "] Test " + iTestName + " timed out."
}
String androidITestBundle(String device){
int testsCount = 0;
int successfulTestsCount = 0;
def tablexiaApk = project.tablexiaAppName + "-iTest-" + "$tablexiaVersionName" + ".apk"
def apkFolder = "${project(':android').projectDir}/build/outputs/apk/"
def sdCardResults = "/sdcard/iTest_results"
println "[iTest-" + device + "] INSTALLING " + tablexiaApk + " into connected Android device"
if(!new File(apkFolder + tablexiaApk).exists()){
throw new GradleException("Could not install " + tablexiaApk + " - file does not exists!")
return
}
def adb = doAndroidSettings()
exec {
commandLine "$adb", '-s', device, 'install', '-r', apkFolder + tablexiaApk
}
println "[iTest-" + device + "] INSTALLING Tablexia COMPLETED"
println "[iTest-" + device + "] CREATING NEW iTest OUTPUT DIRECTORY"
def iTestOutputFolder = "iTest_results-" + device
def iTestOutputFile = new File(apkFolder + iTestOutputFolder)
if (iTestOutputFile.exists()) {
println "[iTest-" + device + "] REMOVING OLD iTest OUTPUT DIRECTORY"
iTestOutputFile.deleteDir()
}
project.file(apkFolder + iTestOutputFolder).mkdirs()
new File("${project(':itest').projectDir}/iTestBundle.txt").eachLine { iTestName ->
exec {
commandLine "$adb", '-s', device, 'shell', 'rm', '-r', sdCardResults
}
println "[iTest-" + device + "] OLD FILES on SDCARD REMOVED"
testsCount++
runTest(iTestName, device)
println "[iTest-" + device + "] TEST: " + iTestName + " FINISHED"
exec {
commandLine "$adb", '-s', device, 'pull', sdCardResults, apkFolder + iTestOutputFolder+ "/"
}
println "[iTest-" + device + "] LOG FILES WERE MOVED INTO " + iTestOutputFile.toString()
}
file("$iTestOutputFile" + "/iTest_results").eachFile { logFile ->
if(logFile.toString().contains("OK")) successfulTestsCount++;
}
if (testsCount != successfulTestsCount){
println "[iTest-" + device + "] ONE OR MORE OF TESTS ON THIS DEVICE FINISHED WITH STATUS FAIL!"
return "[iTest-" + device + "] Failed to complete all iTests! Check iTest log files in: " + iTestOutputFile.toString() + "/iTest_results/\n"
}
else{
println "[iTest-" + device + "] ALL TESTS ON THIS DEVICE FINISHED WITH STATUS OK!"
return ""
}
}
task runAndroidITestBundle (dependsOn:'assembleITest'){
doLast {
def adbOut = new ByteArrayOutputStream()
def adb = doAndroidSettings()
exec {
commandLine "$adb", 'devices'
standardOutput = adbOut
}
def connectedDevices = 0
def exceptions = ""
def threads = []
adbOut.toString().eachLine { line ->
def th = new Thread({ //run devices parallel
if (!line.contains("List") && line.contains("device")) {
connectedDevices++
def device = line.split('\t')[0]
println "Connected device: " + device
def adbLsOut = new ByteArrayOutputStream()
exec {
commandLine "$adb", '-s', device, 'shell', 'ls'
standardOutput = adbLsOut
}
if (adbLsOut.toString().contains("error")) {
exceptions += "[iTest-" + device + "] Device does not meet requirements for running tests. Try to check out if device has SD card and SD card is accessible from computer."
println "[iTest-" + device + "] THIS DEVICE DOES NOT MEET REQUIREMENTS FOR RUNNING TESTS."
} else exceptions += androidITestBundle(device)
}
})
threads << th
}
threads.each { it.start() }
threads.each { it.join() }
if(connectedDevices<1) throw new GradleException("NO CONNECTED DEVICE WAS FOUND!")
if(exceptions!="") throw new GradleException(exceptions)
}
}
\ No newline at end of file
......@@ -57,6 +57,7 @@ import cz.nic.tablexia.game.games.crime_scene.model.bonus.BonusSoundsSetting;
import cz.nic.tablexia.loader.application.ApplicationAtlasManager;
import cz.nic.tablexia.loader.application.ApplicationInternalTextureManager;
import cz.nic.tablexia.loader.application.ApplicationTextManager;
import cz.nic.tablexia.screen.AbstractTablexiaScreen;
import cz.nic.tablexia.shared.model.Game;
import cz.nic.tablexia.shared.model.resolvers.CrimeSceneScoreResolver;
import cz.nic.tablexia.util.Log;
......@@ -88,6 +89,7 @@ public class CrimeSceneGame extends AbstractTablexiaGame<CrimeSceneGameState> {
public static final String EVENT_START_PLAY = "event start play";
public static final String EVENT_FINISH_SOUND = "event finish sound index: ";
public static final String EVENT_ACTION_CONTROLLED = "event control action: ";
public static final String EVENT_HIGHLIGHTED_GO = "higlighted object shown";
public static final int ACTION_CARD_DRAG_SIZE = 100;
......@@ -784,6 +786,7 @@ public class CrimeSceneGame extends AbstractTablexiaGame<CrimeSceneGameState> {
public void setHighlightedGameObject(GameObject highlightedGameObject) {
this.highlightedGameObject = highlightedGameObject;
if(highlightedGameObject!=null) AbstractTablexiaScreen.triggerScenarioStepEvent(EVENT_HIGHLIGHTED_GO);
}
......
......@@ -22,6 +22,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import cz.nic.tablexia.TablexiaSettings;
import cz.nic.tablexia.game.games.crime_scene.CrimeSceneGame;
import cz.nic.tablexia.screen.AbstractTablexiaScreen;
import cz.nic.tablexia.util.ui.actionwidget.Action;
import cz.nic.tablexia.util.ui.actionwidget.ActionsStripWidget;
import cz.nic.tablexia.util.ui.tilemapgenerator.Position;
......@@ -32,6 +33,7 @@ import cz.nic.tablexia.util.ui.tilemapgenerator.Position;
* Created by danilov on 18.4.16.
*/
public class GameObject extends Group implements Action.ActionListener {
public static final String EVENT_DROP_DOWN = "drop down card";
private ActionsStripWidget actionsStripWidget;
private CrimeSceneGame crimeSceneGame;
......@@ -74,7 +76,7 @@ public class GameObject extends Group implements Action.ActionListener {
@Override
public void onActionDrop(Action action, int collidesWithNumber) {
AbstractTablexiaScreen.triggerScenarioStepEvent(EVENT_DROP_DOWN);
if (collidesWithNumber != Action.NO_COLLISION_NUMBER) {
// FIXME: 10/30/17 why to store action number if using ordinal?
if (!crimeSceneGame.isActionAdded(action)) actionsStripWidget.addSelectedAction(GameObjectType.values()[action.getActionNumber()].getActionTexturePath(), collidesWithNumber, crimeSceneGame, action.getActionNumber());
......
......@@ -28,7 +28,6 @@ import cz.nic.tablexia.game.games.crime_scene.gameobject.GameObject;
import cz.nic.tablexia.util.entity.Touch;
public class DesktopCrimeSceneDragListener extends CrimeSceneDragListener {
private Timer timer;
private static final float DELAY = 0.15f;
Touch lastRecordedTouchDown;
......
......@@ -30,6 +30,7 @@ import cz.nic.tablexia.TablexiaSettings;
import cz.nic.tablexia.game.games.in_the_darkness.InTheDarknessDifficulty;
import cz.nic.tablexia.game.games.in_the_darkness.InTheDarknessGame;
import cz.nic.tablexia.game.games.in_the_darkness.action.InTheDarknessActionType;
import cz.nic.tablexia.screen.AbstractTablexiaScreen;
import cz.nic.tablexia.util.ui.actionwidget.Action;
import cz.nic.tablexia.util.ui.actionwidget.Action.ActionListener;
import cz.nic.tablexia.game.games.in_the_darkness.assets.InTheDarknessAssets;
......@@ -49,6 +50,7 @@ import static com.badlogic.gdx.scenes.scene2d.actions.Actions.sequence;
*
*/
public class ActionsWidget extends Group implements ActionListener {
public static final String EVENT_DROP_DOWN = "drop down card";
public enum ActionLayer {
BACKGROUND_LAYER (0),
......@@ -257,6 +259,7 @@ public class ActionsWidget extends Group implements ActionListener {
@Override
public void onActionDrop(Action action, int collidesWithNumber) {
AbstractTablexiaScreen.triggerScenarioStepEvent(EVENT_DROP_DOWN);
if (collidesWithNumber != Action.NO_COLLISION_NUMBER) {
actionsStripWidget.addSelectedAction(action.getTexturePath(), collidesWithNumber, inTheDarknessGame, action.getActionNumber());
currentTutorialStepNumber++;
......
......@@ -72,7 +72,7 @@ public class RunesGame extends AbstractTablexiaGame<RunesGameState> {
public static final String RUNES_GROUP = "runes group";
public static final String HEALTH_BAR = "health bar";
public static final String EVENT_NEXT_ROUND = "next round";
public static final String EVENT_ROUND_READY = "round ready";
private static final int ONE_SECOND_DELAY = 1;
private static final int START_SOUND_BOUND = 8;
......@@ -338,7 +338,12 @@ public class RunesGame extends AbstractTablexiaGame<RunesGameState> {
private void nextRound() {
runesGroup.addAction(Actions.fadeIn(FADE_ANIMATION_DURATION));
runesGroup.addAction(Actions.sequence(Actions.fadeIn(FADE_ANIMATION_DURATION), Actions.run(new Runnable() {
@Override
public void run() {
triggerScenarioStepEvent(EVENT_ROUND_READY);
}
})));
getData().setPhase(RunesGameState.RunesGamePhase.GAME);
timeBar.startGameState();
targetPlate.changeTargets(getData().getRound());
......@@ -347,8 +352,6 @@ public class RunesGame extends AbstractTablexiaGame<RunesGameState> {
holderManager.clearHolders();
runesGroup.clearChildren();
initHolders();
testCountRound++;
triggerScenarioStepEvent(EVENT_NEXT_ROUND+testCountRound);
}
/**
......
......@@ -29,6 +29,7 @@ public interface IRune {
Color DEFAULT_COLOR_BONUS = new Color(0, 0, 0, 0.2f);
float CORRECT_ANIMATION_DURATION = 0.5f;
float WRONG_ANIMATION_DURATION = 0.3f;
String EVENT_ANIMATE_RUNE_DONE = "animation of rune done";
enum AnimationType{
CORRECT(Color.GREEN, CORRECT_ANIMATION_DURATION),
......
......@@ -25,6 +25,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image;
import cz.nic.tablexia.game.common.media.GfxLibrary;
import cz.nic.tablexia.game.games.runes.assets.RuneDefinition;
import cz.nic.tablexia.game.games.runes.helper.RuneDescription;
import cz.nic.tablexia.screen.AbstractTablexiaScreen;
/**
* Created by Vitaliy Vashchenko on 8.4.16.
......@@ -63,8 +64,13 @@ public class Rune extends Image implements IRune {
@Override
public void showAnimation(AnimationType animationType) {
if (!hasActions()) {
addAction(Actions.sequence(Actions.color(animationType.getColor(), animationType.getDuration()), Actions.color(defaultColor, animationType.getDuration())));
addAction(Actions.after(Actions.color(defaultColor)));
addAction(Actions.sequence(Actions.color(animationType.getColor(), animationType.getDuration()), Actions.color(DEFAULT_COLOR, animationType.getDuration())));
addAction(Actions.after(Actions.sequence(Actions.color(DEFAULT_COLOR), Actions.run(new Runnable() {
@Override
public void run() {
AbstractTablexiaScreen.triggerScenarioStepEvent(EVENT_ANIMATE_RUNE_DONE);
}
}))));
}
}
......
......@@ -27,6 +27,7 @@ import cz.nic.tablexia.game.common.TablexiaRandom;
import cz.nic.tablexia.game.common.media.GfxLibrary;
import cz.nic.tablexia.game.games.runes.assets.RuneDefinition;
import cz.nic.tablexia.game.games.runes.helper.RuneDescription;
import cz.nic.tablexia.screen.AbstractTablexiaScreen;
/**
* Created by Vitaliy Vashchenko on 27.4.16.
......@@ -111,8 +112,13 @@ public class RunesCluster extends Group implements IRune {
private void animateRune(Rune rune, AnimationType animationType) {
if (!rune.hasActions()) {
rune.addAction(Actions.sequence(Actions.color(animationType.getColor()), Actions.color(defaultColor, animationType.getDuration())));
rune.addAction(Actions.after(Actions.color(defaultColor)));
rune.addAction(Actions.sequence(Actions.color(animationType.getColor()), Actions.color(DEFAULT_COLOR, animationType.getDuration())));
rune.addAction(Actions.after(Actions.sequence(Actions.color(DEFAULT_COLOR), Actions.run(new Runnable() {
@Override
public void run() {
AbstractTablexiaScreen.triggerScenarioStepEvent(EVENT_ANIMATE_RUNE_DONE);
}
}))));
}
}
......
......@@ -111,6 +111,9 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
public static final String SCENARIO_STEP_MUGSHOTS_VISIBLE = "mugshot images visible";
public static final String SCENARIO_STEP_IMAGE_CHOSEN = "image chosen";
public static final String SCENARIO_STEP_TEXT_FIELD_TIMEOUT = "text field timeout";
public static final String PEN_SHAKED = "pen shaked";
public static final String ALTERNATIVE_TEXT_FIELD_SHOWN = "alternative text field shown";
public static final String ALTERNATIVE_NAME_FIELD_HIDDEN = "alternative name field hidden";
public static final String FORM_SIGNATURE_DIALOG = "signature dialog";
......@@ -122,6 +125,8 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
public static final String FORM_SIGNATURE = "formSignature";
public static final String FORM_AVATAR_IMAGE = "avatar image";
public static final String FORM_STAMP = "formStamp";
public static final String ALTERNATIVE_TEXT_FIELD = "alternative text field";
public static final String YES_ICON = "yes icon";
private static final int PRIVACY_LABEL_WIDTH = 340;
private static final int PRIVACY_LABEL_HEIGHT = 40;
......@@ -360,6 +365,7 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
if(TablexiaSettings.getInstance().getPlatform() != TablexiaSettings.Platform.DESKTOP) {
Gdx.input.setOnscreenKeyboardVisible(true);
showAlternativeTextField();
triggerScenarioStepEvent(ALTERNATIVE_TEXT_FIELD_SHOWN);
}
}
});
......@@ -542,6 +548,7 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
alternativeTextField.setPosition(getStage().getWidth()/2 - alternativeTextField.getWidth()/2, 3*getStage().getHeight()/5);
alternativeTextField.setFocusTraversal(false);
alternativeTextField.setMaxLength(MAX_NAME_LENGTH);
alternativeTextField.setName(ALTERNATIVE_TEXT_FIELD);
alternativeTextField.addListener(new InputListener() {
@Override
public boolean keyTyped(InputEvent event, char character) {
......@@ -570,6 +577,7 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
noIcon.setPosition(alternativeTextField.getX() - PRIVACY_LABEL_OFFSET_X - noIcon.getWidth(), alternativeTextField.getY());
yesIcon = new Image(getScreenTextureRegion(FormScreenAssets.YES_ICON));
yesIcon.setName(YES_ICON);
yesIcon.addListener(new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
......@@ -577,6 +585,7 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
hideAlternativeField();
validate();
Gdx.input.setOnscreenKeyboardVisible(false);
triggerScenarioStepEvent(ALTERNATIVE_NAME_FIELD_HIDDEN);
}
});
yesIcon.setPosition(alternativeTextField.getX() + alternativeTextField.getWidth() + PRIVACY_LABEL_OFFSET_X, alternativeTextField.getY());
......@@ -669,7 +678,12 @@ public class FormScreen extends AbstractTablexiaScreen<Void> {
if (isValid(FormValidationEnum.SIGNATURE) == false) {
displayHintDialog(FormValidationEnum.SIGNATURE, signaturePlace);
playValidationSound(FormScreenAssets.VALIDATION_SIGNATURE_SOUND);
pen.addAction(Actions.forever(Actions.sequence(Actions.repeat(SHAKE_ANIMATION_REPEAT_COUNT, Actions.sequence(Actions.moveBy(SHAKE_ANIMATION_OFFSET, 0, SHAKE_ANIMATION_STEP_DURATION), Actions.moveBy(-SHAKE_ANIMATION_OFFSET, 0, SHAKE_ANIMATION_STEP_DURATION))), Actions.delay(1))));
pen.addAction(Actions.forever(Actions.sequence(Actions.repeat(SHAKE_ANIMATION_REPEAT_COUNT, Actions.sequence(Actions.moveBy(SHAKE_ANIMATION_OFFSET, 0, SHAKE_ANIMATION_STEP_DURATION), Actions.moveBy(-SHAKE_ANIMATION_OFFSET, 0, SHAKE_ANIMATION_STEP_DURATION))), Actions.delay(1), Actions.run(new Runnable() {
@Override
public void run() {
triggerScenarioStepEvent(PEN_SHAKED);
}
}))));
return false;
}
......
# <b>How to run intergation tests</b>
## <b>Run selected test</b>
Use This option to run only one selected test on one device.
Available tests are in tablexia/itest/listOfAvailableITests.txt.
<b>Desktop version</b>
Run selected test (from tablexia/desktop/build/libs):
Run selected test (jar file is in tablexia/desktop/build/libs):
```
java -jar Tablexia\ \(feature-gitlab-ci\)-iTest-<VERSION>.jar <TEST_CLASS_NAME>
```
......@@ -19,11 +22,11 @@ You can run the test from Android Studio using Edit Configurations -> Launch Fl
-e testClassName <TEST_CLASS_NAME>
```
and build variant iTest.
Log file will be saved into tablexia/android/build/outputs/apk/iTest_results.
Log file will be saved into external memory of connected device in to a folder iTest_results.
<b>iOS version</b>
For running tests on iOS devices, you need an utility "ideviceinstaller"(http://macappstore.org/ideviceinstaller/)
For running tests on iOS devices, you need a utility "ideviceinstaller"(http://macappstore.org/ideviceinstaller/)
Command ```ideviceinstaller -l``` shows installed applications on your connected iOS device.
For example :
......@@ -37,31 +40,32 @@ For example:
```
idevicedebug run "cz.nic.tablexia.devel" "myParameter"
```
If there are more than one connected iOS device, you need to specify what device you want to use using parameter:
If there are more than one connected iOS devices, you need to specify what device you want to use using parameter:
```
-u "<FORTY_LOCAL_UDID_OF_DEVICE>"
```
<br />
### <b>Run tests using gradle</b>
### <b>Run set of tests using gradle</b>
Put tests, that you want to run in tablexia/itest/iTestBundle.txt (each one to one line)
You can use file listOfAvailableITests.txt to see which tests are available to use or you can use prepared sets in tablexia/itest/testSets.
<b>Desktop version</b>
Put tests, that you want to run in tablexia/itest/iTestBundle.txt (each one to one line)
You can use file listOfAvailableITests.txt to see which tests are able to use.
Run using gradle:
Command for run desktop tests:
```
./gradlew desktop:runITestBundle
```
or commit and push changes to gitlab.
Tests will be run one by one as they are written in tablexia/itest/iTestBundle.txt and log files will be saved into tablexia/desktop/build/libs/iTest_results.
<b>Android version</b>
For running tests, you must have installed apk (iTest version) in your connected Android device.
Put tests, that you want to run in tablexia/itest/iTestBundle.txt (each one to one line)
For running tests one by one on Android device, use script runAndroidITests.sh (from tablexia/itest):
You can run set of tests on one or more connected devices at the same time.
For running tests, you must have set all connected devices to developer mode and have permition to USB debugging.
Command for run android tests:
```
./runAndroidITests.sh
./gradlew runAndroidITestBundle
```
Tests will be run one by one as they are written in tablexia/itest/iTestBundle.txt and log files will be saved into tablexia/android/build/outputs/apk/iTest_results.
Tests will be run one by one as they are written in tablexia/itest/iTestBundle.txt and log files will be saved into tablexia/android/build/outputs/apk/iTest_results-<DEVICENAME>/iTest_results.
TestGameKidnappingEasyNoneStar
TestGameKidnappingEasyOneStar
NewUserScenario
TestScenarioSpecial
TestScenarioHallOfFameRobberyTrophies
TestScenarioHeapOfTrophies
TestGameShootingRangeEasyNoneStar
TestGameRobberyMediumOneStar
TestGameInTheDarknessMedium
TestGameNightWatchMediumNoneStar
TestGamePursuitHardThreeStar
TestGameCrimeSceneHardOneStar
TestGameKidnappingEasyTwoStar
TestGameKidnappingEasyThreeStar
TestGameKidnappingMediumNoneStar
TestGameKidnappingMediumOneStar
TestGameKidnappingMediumTwoStar
TestGameKidnappingMediumThreeStar
TestGameKidnappingHardNoneStar
TestGameKidnappingHardOneStar
TestGameKidnappingHardTwoStar
TestGameKidnappingHardThreeStar
\ No newline at end of file
TestGameRunesMediumThreeStar
TestGameProtocolEasyOneStar
TestGameSafeHardNoneStars
TestStatisticsInTheDarkness
TestEncyclopedia
TestProfile
OfficeTestScenario
\ No newline at end of file
NewUserScenario
TestScenarioSpecial
TestScenarioHallOfFameRobberyTrophies
TestScenarioHallOfFamePursuitTrophies
TestScenarioHallOfFameKidnappingTrophies
TestScenarioHallOfFameNightWatchTrophies
TestScenarioHallOfFameShootingRangeTrophies
TestScenarioHallOfFameInTheDarknessTrophies
TestScenarioHallOfFameCrimeSceneTrophies
TestScenarioHallOfFameRunesTrophies
TestScenarioHeapOfTrophies
TestGameShootingRangeEasyNoneStar
TestGameShootingRangeEasyOneStar
TestGameShootingRangeEasyTwoStar
TestGameShootingRangeEasyThreeStar
TestGameShootingRangeMediumNoneStar
TestGameShootingRangeMediumOneStar
TestGameShootingRangeMediumTwoStar
TestGameShootingRangeMediumThreeStar
TestGameShootingRangeHardNoneStar
TestGameShootingRangeHardOneStar
TestGameShootingRangeHardTwoStar
TestGameShootingRangeHardThreeStar
TestGameRobberyEasyNoneStar
TestGameRobberyEasyOneStar
TestGameRobberyEasyTwoStar
TestGameRobberyEasyThreeStar
TestGameRobberyMediumNoneStar
TestGameRobberyMediumOneStar
TestGameRobberyMediumTwoStar
TestGameRobberyMediumThreeStar
TestGameRobberyHardNoneStar
TestGameRobberyHardOneStar
TestGameRobberyHardTwoStar
TestGameRobberyHardThreeStar
TestGameInTheDarknessEasy
TestGameInTheDarknessMedium
TestGameInTheDarknessHard
TestGameInTheDarknessEasyScenario
TestGameInTheDarknessMediumScenario
TestGameInTheDarknessHardScenario
TestGameNightWatchEasyNoneStar
TestGameNightWatchEasyOneStar
TestGameNightWatchEasyTwoStar
TestGameNightWatchEasyThreeStar
TestGameNightWatchMediumNoneStar
TestGameNightWatchMediumOneStar
TestGameNightWatchMediumTwoStar
TestGameNightWatchMediumThreeStar
TestGameNightWatchHardNoneStar
TestGameNightWatchHardOneStar
TestGameNightWatchHardTwoStar
TestGameNightWatchHardThreeStar
TestGamePursuitEasyNoneStar
TestGamePursuitEasyOneStar
TestGamePursuitEasyTwoStar
TestGamePursuitEasyThreeStar
TestGamePursuitMediumNoneStar
TestGamePursuitMediumOneStar
TestGamePursuitMediumTwoStar
TestGamePursuitMediumThreeStar
TestGamePursuitHardNoneStar
TestGamePursuitHardOneStar
TestGamePursuitHardTwoStar
TestGamePursuitHardThreeStar
TestGameCrimeSceneEasyNoneStar
TestGameCrimeSceneEasyOneStar
TestGameCrimeSceneEasyTwoStar
TestGameCrimeSceneEasyThreeStar
TestGameCrimeSceneMediumNoneStar
TestGameCrimeSceneMediumOneStar
TestGameCrimeSceneMediumTwoStar
TestGameCrimeSceneMediumThreeStar
TestGameCrimeSceneHardNoneStar
TestGameCrimeSceneHardOneStar
TestGameCrimeSceneHardTwoStar
TestGameCrimeSceneHardThreeStar
TestGameCrimeSceneBonusNoneStar
TestGameCrimeSceneBonusOneStar
TestGameCrimeSceneBonusTwoStar
TestGameCrimeSceneBonusThreeStar
TestGameKidnappingEasyNoneStar
TestGameKidnappingEasyOneStar
TestGameKidnappingEasyTwoStar
TestGameKidnappingEasyThreeStar
TestGameKidnappingMediumNoneStar
TestGameKidnappingMediumOneStar
TestGameKidnappingMediumTwoStar
TestGameKidnappingMediumThreeStar
TestGameKidnappingHardNoneStar
TestGameKidnappingHardOneStar
TestGameKidnappingHardTwoStar
TestGameKidnappingHardThreeStar
TestGameRunesEasyNoneStar
TestGameRunesEasyOneStar
TestGameRunesEasyTwoStar
TestGameRunesEasyThreeStar
TestGameRunesMediumNoneStar
TestGameRunesMediumOneStar
TestGameRunesMediumTwoStar
TestGameRunesMediumThreeStar
TestGameRunesHardNoneStar
TestGameRunesHardOneStar
TestGameRunesHardTwoStar
TestGameRunesHardThreeStar
TestGameProtocolEasyNoneStar
TestGameProtocolEasyOneStar
TestGameProtocolEasyTwoStar
TestGameProtocolEasyThreeStar
TestGameProtocolMediumNoneStar
TestGameProtocolMediumOneStar
TestGameProtocolMediumTwoStar
TestGameProtocolMediumThreeStar
TestGameProtocolHardNoneStar
TestGameProtocolHardOneStar
TestGameProtocolHardTwoStar
TestGameProtocolHardThreeStar
TestGameSafeEasyNoneStars
TestGameSafeEasyOneStars
TestGameSafeEasyTwoStars
TestGameSafeEasyThreeStars
TestGameSafeMediumNoneStars
TestGameSafeMediumOneStar
TestGameSafeMediumTwoStars
TestGameSafeMediumThreeStars
TestGameSafeHardNoneStars
TestGameSafeHardOneStar
TestGameSafeHardTwoStars
TestGameSafeHardThreeStars
TestStatisticsButtons
TestStatisticsRobbery
TestStatisticsCrimeScene
TestStatisticsInTheDarkness
TestStatisticsKidnapping
TestStatisticsNightWatch
TestStatisticsPursuit
TestStatisticsRunes
TestStatisticsShootingRange
TestStatisticsSafe
TestStatisticsProtocol
TestEncyclopedia
TestProfile
OfficeTestScenario
\ No newline at end of file
NewUserScenario
TestScenarioSpecial
TestScenarioHallOfFameRobberyTrophies
TestScenarioHallOfFamePursuitTrophies
TestScenarioHallOfFameKidnappingTrophies
TestScenarioHallOfFameNightWatchTrophies
TestScenarioHallOfFameShootingRangeTrophies
TestScenarioHallOfFameInTheDarknessTrophies
TestScenarioHallOfFameCrimeSceneTrophies
TestScenarioHallOfFameRunesTrophies
TestScenarioHallOfFameProtocolTrophies
TestScenarioHallOfFameSafeTrophies
TestScenarioHeapOfTrophies
TestGameShootingRangeEasyNoneStar
TestGameShootingRangeEasyOneStar
TestGameShootingRangeEasyTwoStar
TestGameShootingRangeEasyThreeStar
TestGameShootingRangeMediumNoneStar
TestGameShootingRangeMediumOneStar
TestGameShootingRangeMediumTwoStar
TestGameShootingRangeMediumThreeStar
TestGameShootingRangeHardNoneStar
TestGameShootingRangeHardOneStar
TestGameShootingRangeHardTwoStar
TestGameShootingRangeHardThreeStar
TestGameRobberyEasyNoneStar
TestGameRobberyEasyOneStar
TestGameRobberyEasyTwoStar
TestGameRobberyEasyThreeStar
TestGameRobberyMediumNoneStar
TestGameRobberyMediumOneStar
TestGameRobberyMediumTwoStar
TestGameRobberyMediumThreeStar
TestGameRobberyHardNoneStar
TestGameRobberyHardOneStar
TestGameRobberyHardTwoStar
TestGameRobberyHardThreeStar
TestGameInTheDarknessEasy
TestGameInTheDarknessMedium
TestGameInTheDarknessHard
TestGameInTheDarknessEasyScenario
TestGameInTheDarknessMediumScenario
TestGameInTheDarknessHardScenario
TestGameNightWatchEasyNoneStar
TestGameNightWatchEasyOneStar
TestGameNightWatchEasyTwoStar
TestGameNightWatchEasyThreeStar
TestGameNightWatchMediumNoneStar
TestGameNightWatchMediumOneStar
TestGameNightWatchMediumTwoStar
TestGameNightWatchMediumThreeStar
TestGameNightWatchHardNoneStar
TestGameNightWatchHardOneStar
TestGameNightWatchHardTwoStar
TestGameNightWatchHardThreeStar
TestGamePursuitEasyNoneStar
TestGamePursuitEasyOneStar
TestGamePursuitEasyTwoStar
TestGamePursuitEasyThreeStar
TestGamePursuitMediumNoneStar
TestGamePursuitMediumOneStar
TestGamePursuitMediumTwoStar
TestGamePursuitMediumThreeStar
TestGamePursuitHardNoneStar
TestGamePursuitHardOneStar
TestGamePursuitHardTwoStar
TestGamePursuitHardThreeStar
TestGameCrimeSceneEasyNoneStar
TestGameCrimeSceneEasyOneStar
TestGameCrimeSceneEasyTwoStar
TestGameCrimeSceneEasyThreeStar
TestGameCrimeSceneMediumNoneStar
TestGameCrimeSceneMediumOneStar
TestGameCrimeSceneMediumTwoStar
TestGameCrimeSceneMediumThreeStar
TestGameCrimeSceneHardNoneStar
TestGameCrimeSceneHardOneStar
TestGameCrimeSceneHardTwoStar
TestGameCrimeSceneHardThreeStar
TestGameKidnappingEasyNoneStar
TestGameKidnappingEasyOneStar
TestGameKidnappingEasyTwoStar
TestGameKidnappingEasyThreeStar