You are on page 1of 42

Test Automation Framework for Philips SmartSleep

and Philips Somneo


Submitted in partial fulfilment of the requirements for the degree of

Master of Technology
In
Software Engineering
by
Darsi Kapil
Register Number
14MSE0032
Under the guidance of
Prof.Magesh G
Assistant Professor(Junior)
School of Information Technology and Engineering
VIT, Vellore.

April, 2019

1
DECLARATION

I hereby declare that the thesis entitled “Test Automation Framework for

Philips SmartSleep and Philips Somneo” by me, for the award of the degree of

Master of Technology in Software Engineering to VIT is a record of bonafide

work carried out by me under the supervision of Prof.Magesh G(Assistant

Professor(Junior)).

I further declare that the work reported in this thesis has not been submitted and

will not be submitted, either in part or in full, for the award of any other degree

or diploma in this institute or any other institute or university.

Place : Vellore
Date : Signature of the Candidate

2
CERTIFICATE

This is to certify that the thesis entitled “Test Automation Framework for
Philips SmartSleep and Philips Somneo” submitted by “Darsi Kapil,
14MSE0032”, School of Information Technology and Engineering, VIT
University, for the award of the degree of Master of Technology in Software
Engineering, is a record of bonafide work carried out by him under my
supervision during the period, 01. 12. 2018 to 30.04.2019, as per the VIT code of
academic and research ethics. The contents of this report have not been submitted
and will not be submitted either in part or in full, for the award of any other degree
or diploma in this institute or any other institute or university. The thesis fulfils
the requirements and regulations of the University and in my opinion meets the
necessary standards for submission.

Place : Vellore
Date : Signature of the Guide

Internal Examiner External Examiner

Head of Department

MTech Software Engineering

3
INDUSTRY CERTIFICATE

This is to certify that the thesis entitled “Test Automation Framework for Philips
SmartSleep and Philips Somneo” submitted by “Darsi Kapil, 14MSE0032”, School

of Information Technology and Engineering, VIT University, for the award of the
degree of Master of Technology in Software Engineering, is a record of bonafide
work carried out by him under my supervision during the period, 02.08.2018 –
Current Date, as per the Philips Business Principles and Guidelines. The contents
of this report have not been submitted and will not be submitted either in part or
in full, for the award of any other degree or diploma in this institute or any other
institute or university. The thesis fulfils the requirements and regulations of the
University and in my opinion meets the necessary standards for submission.

Place : Vellore
Date : Signature of the Mentor

4
TABLE OF CONTENTS
CHAPTER NO. TITLE PAGE NO.
1. INTRODUCTION 7
1.1.Problem Statement 7
1.2.Motivation 7
1.3.Objective 7
2. TECHNOLOGIES LEARNT 8
2.1 SeeTest Automation 8
2.2 Specflow 8
2.3 Jenkins 10
2.4 SourceTree 12
3. PRODUCT INFORMATION 12
3.1 Philips Smartsleep 12
3.2 Philps Somneo 13
3.3 Philips SleepMapper Application 15
3.3.1 Application User Interface 16
3.3.2 Application Splash Screen/Launch Screen 16
3.3.3 Product Selection Screen 16
3.3.4 Wi-Fi Set-up screen for Philips Somneo 17
3.3.5 Bluetooth Setup for Philips SmartSleep 18
3.3.6 Bluetooth Device Discovery Screen 19
3.3.7 Sleep Data Screen 19
4. IMPLEMENTATION 20
4.1 HiggsTest.py 20
4.2 UserRegistration.Feature 25
4.3 UserRegistration.cs 25
4.4 AppLaucher.cs 26
4.5 ConfigurationManager.cs 30
4.6 Hooks.cs 34

5
4.7 PowerSleepDevice.cs 36
4.8 ScenarioContext.cs 38
4.9 ScreenFlow.cs 40
6. RESULTS AND DISCUSSIONS 42
7. CONCLUSION AND FUTURE WORK 42
7.1 Conclusion 42
7.2 Future Work 42

6
1 . INTRODUCTION

1.1. Problem Statement


Sleep deprivation occurs when an individual gets less sleep than they need to feel awake
and alert. People vary in how little sleep is needed to be considered sleep-deprived.
Some people such as older adults seem to be more resistant to the effects of sleep
deprivation, while others, especially children and young adults, are more vulnerable.

Although occasional sleep interruptions are generally no more than a nuisance, ongoing
lack of sleep can lead to excessive daytime sleepiness, emotional difficulties, poor job
performance, Obesity and a lowered perception of quality of life.

1.2. Motivation
Millions of people know that sleep is critical to their health, and are searching for ways
to get better rest. From identifying individual sleep needs to offering clinically proven
solutions, Philips is on a mission to make better sleep a reality for everyone. For 35 years,
Philips has developed revolutionary sleep products rooted in clinical evidence and data-driven
insights, making them the proud leader in sleep innovation. Their portfolio continues to grow
– and are expanding their reach in order to help people everywhere sleep better and live happier,
healthier lives.

1.3. Objective
Our main objective is develop a automation framework using SeeTest Automation
which could help to automate the test cases and then integrate them into the Jenkins CI and run
the automation script on a nightly basis to see if there is any flaw in the build or the automation
script.

7
2.TECHNOLOGIES INVOLVED

2.1 SeeTest Automation:

In general, the process of working with SeeTest Automation mobile testing begins with
connecting either a physical mobile device or a corresponding emulator (on-site cloud
configuration is also available). Once connected, you can record and edit scripts which will
perform a specific test scenario. When each script is complete, you can play it, evaluate the
generated execution report, and export the selected test code to any testing framework that
you regularly use. SeeTest Automation , Supports many test frameworks including; UFT,
TestComplete, RFT, C#, JUnit, Python, and Perl. From each of these, you can perform
additional editing prior to running each test. Then simply execute the test, and review the
reports generated by each individual framework. Cover mobile performance testing and
functional testing all at once. Run your existing mobile automated test cases under different
network, device and load conditions. Define and inject different network profiles to your
tests. Simulate different parameters such as the geographical location of a user, server and
network type (3G,4G, LTE,etc.) Make sure your app does not overly consume device
resources or crashes mobile devices by checking the usage levels of critical features such as
CPU, memory, battery. Run your mobile test automation suites under different load
conditions by integrating with HP LoadRunner.

2.2 Specflow:
We use SpecFlow to define, manage and automatically execute human-readable acceptance
tests in .NET projects. Writing easily understandable tests is a cornerstone of the BDD
paradigm and also helps build up a living documentation of your system.

SpecFlow is open source and provided under a BSD license. As part of the Cucumber family,
SpecFlow uses the official Gherkin parser and supports the .NET framework, Xamarin and
Mono.

SpecFlow integrates with Visual Studio, but can be also used from the command line (e.g. on
a build server). SpecFlow supports popular testing frameworks: MSTest, NUnit (2 and 3) and
xUnit 2.

8
SpecFlow+ adds additional functionality to SpecFlow, such as Visual Studio Test Explorer
integration, a dedicated test runner with advanced test execution options, execution reports
(HTML, XML, JSON) and much more.

We use three basic steps to automate our test cases:

1) Specify

Describe the behavior of your system using human-readable syntax. Define specifications in
the problem domain using the language of your stakeholders and build up a living
documentation of your system.

Fig 2.1

2) Automate
Bind your test specifications to your application code to automate the testing of your system.

9
Fig 2.2

3) Execute
Execute the test cases and integrate them into the Jenkins CI server.

2.3 Jenkins:
Jenkins is a self-contained, open source automation server which can be used to automate all
sorts of tasks related to building, testing, and delivering or deploying software.

Jenkins can be installed through native system packages, Docker, or even run standalone by
any machine with a Java Runtime Environment (JRE) installed.

Pipelines are made up of multiple steps that allow you to build, test and deploy applications.
Jenkins Pipeline allows you to compose multiple steps in an easy way that can help you model
any sort of automation process. Think of a "step" like a single command which performs a
single action. When a step succeeds it moves onto the next step. When a step fails to execute
correctly the Pipeline will fail. When all the steps in the Pipeline have successfully completed,
the Pipeline is considered to have successfully executed.

10
Fig 2.3

2.4 SourceTree:
Version control systems are a category of software tools that help a software team manage
changes to source code over time. Version control software keeps track of every modification
to the code in a special kind of database. If a mistake is made, developers can turn back the
clock and compare earlier versions of the code to help fix the mistake while minimizing
disruption to all team members.

For almost all software projects, the source code is like the crown jewels - a precious asset
whose value must be protected. For most software teams, the source code is a repository of
the invaluable knowledge and understanding about the problem domain that the developers
have collected and refined through careful effort. Version control protects source code from
both catastrophe and the casual degradation of human error and unintended consequences.

Software developers working in teams are continually writing new source code and changing
existing source code. The code for a project, app or software component is typically
organized in a folder structure or "file tree". One developer on the team may be working on a
new feature while another developer fixes an unrelated bug by changing code, each developer
may make their changes in several parts of the file tree.

11
Fig 2.4

3.PRODUCT INFORMATION

3.1. Philips SmartSleep


Beyond sleep tracking, the sleep-enhancing headband and sleep app is a complete solution that
actively improves your sleep. During the night our bodies cycle through different stages of
sleep: rapid-eye movement (or REM), and non-REM sleep, which has three stages. When your
body is in the third stage of non-REM – deep sleep – your heartbeat and breathing slow to their
lowest levels and muscles relax. This is the most restorative stage of sleep often called "slow
wave sleep". Once in “slow wave sleep”, the algorithm triggers quiet audio tones to boost these
slow waves, thus improving quality of sleep.Two small sensors from the headband detect when
you are in deep sleep.

It monitors your level of sleep from moment to moment and responds in real time.Every night,
our proprietary algorithm customizes the timing and volume of tones to your sleep
pattern.Volume is controlled not to wake you up, and Bluetooth and Wi-Fi are off when device
is being worn.Every morning, the SleepMapper App will show you your sleep metrics and your
sleep boost score. The Total Sleep Score is an aggregate of several dimensions of your sleep.
It includes the total amount of time that you sleep, how long it takes you to fall asleep, the
number of awakenings that occur while you sleep and the Sleep Boost. Each component value

12
is either adding to or detracting from your best sleep. We help you to see where this is occurring
so that you can actively manage to improve your sleep quality. Getting more total sleep is your
best bet to improving your overall total sleep score.

Fig 2.5

3.2. Philips Somneo


Inspired by nature's sunrise, light gradually increases before your wake time from soft morning
red through orange, until your room is filled with bright yellow light. The process of changing
and increasing light is designed to stimulate your body to wake up naturally, as light gently
prepares your body for waking up while you are still asleep. By the time light has filled the
room, your selected natural sound or radio station completes the wake up experience, leaving
you ready for your day.nspired by well-known breathing and relaxation exercises our light-
guided wind-down function is designed to help get you to sleep by helping you decompress
from the day’s activities and unwind. Follow one of seven rhythms of light intensity or sound
with your breathing while keeping your eyes closed for a calm and peaceful transition from
your day to your dreams. The sunset simulation program prepares your body to sleep by
gradually decreasing light and optional sound to your set duration, gently helping you to relax
before you fall asleep.

13
Fig 2.6

14
3.3. Philips SleepMapper Application
Philips SleepMapper Application was developed to control the SmartSleep and Somneo
devices by improving the usability of the devices.

15
3.3. Application User Interface

3.3.1. Splash Screen

16
3.3.2. Launch Screen and Login Screen

3.3.3. Product Selection Screen

17
3.3.4 WiFi Setup for Philips Somneo Screen

3.3.5 Bluetooth Setup for Philips SmartSleep Screen

18
3.3.6 Discover Devices Screen for Philips SmartSleep

3.3.7 Sleep Data Screen

19
4.IMPLEMENTATION OF THE FRAMEWORK

4.1 Higgs test.py

from __future__ import absolute_import

from __future__ import division

from __future__ import print_function

import os

import tempfile

import numpy as np

import pandas as pd

import tensorflow as tf

# pylint: disable=g-bad-import-order

from official.boosted_trees import train_higgs

from official.utils.testing import integration

TEST_CSV = os.path.join(os.path.dirname(__file__), "train_higgs_test.csv")

tf.logging.set_verbosity(tf.logging.ERROR)

class BaseTest(tf.test.TestCase):

"""Tests for Wide Deep model."""

@classmethod

def setUpClass(cls): # pylint: disable=invalid-name

super(BaseTest, cls).setUpClass()

train_higgs.define_train_higgs_flags()

def setUp(self):

# Create temporary CSV file

20
self.data_dir = self.get_temp_dir()

data = pd.read_csv(

TEST_CSV, dtype=np.float32, names=["c%02d" % i for i in range(29)]

).as_matrix()

self.input_npz = os.path.join(self.data_dir, train_higgs.NPZ_FILE)

# numpy.savez doesn't take gfile.Gfile, so need to write down and copy.

tmpfile = tempfile.NamedTemporaryFile()

np.savez_compressed(tmpfile, data=data)

tf.gfile.Copy(tmpfile.name, self.input_npz)

def test_read_higgs_data(self):

"""Tests read_higgs_data() function."""

# Error when a wrong data_dir is given.

with self.assertRaisesRegexp(RuntimeError, "Error loading data.*"):

train_data, eval_data = train_higgs.read_higgs_data(

self.data_dir + "non-existing-path",

train_start=0, train_count=15, eval_start=15, eval_count=5)

# Loading fine with the correct data_dir.

train_data, eval_data = train_higgs.read_higgs_data(

self.data_dir,

train_start=0, train_count=15, eval_start=15, eval_count=5)

self.assertEqual((15, 29), train_data.shape)

self.assertEqual((5, 29), eval_data.shape)

def test_make_inputs_from_np_arrays(self):

21
"""Tests make_inputs_from_np_arrays() function."""

train_data, _ = train_higgs.read_higgs_data(

self.data_dir,

train_start=0, train_count=15, eval_start=15, eval_count=5)

(input_fn, feature_names,

feature_columns) = train_higgs.make_inputs_from_np_arrays(

features_np=train_data[:, 1:], label_np=train_data[:, 0:1])

# Check feature_names.

self.assertAllEqual(feature_names,

["feature_%02d" % (i+1) for i in range(28)])

# Check feature columns.

self.assertEqual(28, len(feature_columns))

bucketized_column_type = type(

tf.feature_column.bucketized_column(

tf.feature_column.numeric_column("feature_01"),

boundaries=[0, 1, 2])) # dummy boundaries.

for feature_column in feature_columns:

self.assertIsInstance(feature_column, bucketized_column_type)

# At least 2 boundaries.

self.assertGreaterEqual(len(feature_column.boundaries), 2)

# Tests that the source column names of the bucketized columns match.

self.assertAllEqual(feature_names,

[col.source_column.name for col in feature_columns])

22
# Check features.

features, labels = input_fn().make_one_shot_iterator().get_next()

with tf.Session() as sess:

features, labels = sess.run((features, labels))

self.assertIsInstance(features, dict)

self.assertAllEqual(feature_names, sorted(features.keys()))

self.assertAllEqual([[15, 1]] * 28,

[features[name].shape for name in feature_names])

# Validate actual values of some features.

self.assertAllClose(

[0.869293, 0.907542, 0.798834, 1.344384, 1.105009, 1.595839,

0.409391, 0.933895, 1.405143, 1.176565, 0.945974, 0.739356,

1.384097, 1.383548, 1.343652],

np.squeeze(features[feature_names[0]], 1))

self.assertAllClose(

[-0.653674, -0.213641, 1.540659, -0.676015, 1.020974, 0.643109,

-1.038338, -2.653732, 0.567342, 0.534315, 0.720819, -0.481741,

1.409523, -0.307865, 1.474605],

np.squeeze(features[feature_names[10]], 1))

def test_end_to_end(self):

"""Tests end-to-end running."""

model_dir = os.path.join(self.get_temp_dir(), "model")

integration.run_synthetic(

main=train_higgs.main, tmp_root=self.get_temp_dir(), extra_flags=[

"--data_dir", self.data_dir,

23
"--model_dir", model_dir,

"--n_trees", "5",

"--train_start", "0",

"--train_count", "12",

"--eval_start", "12",

"--eval_count", "8",

],

synth=False, max_train=None)

self.assertTrue(tf.gfile.Exists(os.path.join(model_dir, "checkpoint")))

def test_end_to_end_with_export(self):

"""Tests end-to-end running."""

model_dir = os.path.join(self.get_temp_dir(), "model")

export_dir = os.path.join(self.get_temp_dir(), "export")

integration.run_synthetic(

main=train_higgs.main, tmp_root=self.get_temp_dir(), extra_flags=[

"--data_dir", self.data_dir,

"--model_dir", model_dir,

"--export_dir", export_dir,

"--n_trees", "5",

"--train_start", "0",

"--train_count", "12",

"--eval_start", "12",

"--eval_count", "8",

],

synth=False, max_train=None)

24
self.assertTrue(tf.gfile.Exists(os.path.join(model_dir, "checkpoint")))

self.assertTrue(tf.gfile.Exists(os.path.join(export_dir)))

if __name__ == "__main__":

tf.test.main()

4.2 UserRegistration.feature

Feature: UserRegistration
In order to be able to use Philips services
As a Healthy Sleep User
I want to be able to create and log in to a Philips account

Background:

Given User account 'deku@mailinator.com' is wiped

@UserRegistrationLogIn
Scenario: Log in with existing account
Given the app is freshly installed
And the app is started
And I am on screen 'UserRegistration'
When I press the button 'Philips account'
Then I should see the text 'Log in'
When I enter text 'deku@mailinator.com' in the field 'Your email or phone
number'
And I enter text 'password123' in the field 'Enter password'
And I click the text 'deku@mailinator.com'
When I press the button 'Log In'
Then I should wait for UserRegistration to login
When I press the button 'I accept the Terms and Conditions'
And I press the button 'Continue’
Then I should go to the next screen

4.3 UserRegistration.cs
using System;
using TechTalk.SpecFlow;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using PowerSleep_TestAutomation.Framework;
using PowerSleep_TestAutomation.Framework.SeeTest;

namespace PowerSleep_TestAutomation.StepDefinitions
{
[Binding]
public class UserRegistrationSteps : Steps
{
private static string SUBTITLE_FIELD = "UserRegistration LogIn SubTitle";
private static string COUNTRY_SELECT = "Country Selection";

25
[Then(@"I should wait for UserRegistration to login")]
public void ThenIShouldWaitForUserRegistrationToLogIn()
{
string zone =
(string)ScenarioContext.Current[ScenarioContextKeys.CURRENT_SCREEN];
var client = SeeTestClientManager.GetClient();

// first check if the spinner appears


string spinner = @"Log In Spinner";
client.WaitForElement(zone, spinner, 0, 1000);
client.WaitForElementToVanish(zone, spinner, 0, 60000);
}

[When(@"I select '(.*)' as my country")]


public void WhenISelectMyCountry(string myCountry)
{
string zone =
(string)ScenarioContext.Current[ScenarioContextKeys.CURRENT_SCREEN];
var client = SeeTestClientManager.GetClient();

// first click the current country (stored in config)


string executingCountry =
ConfigurationManager.Instance.GetStringConfigurationProperty(ConfigurationManager.HSS_
EXECUTING_COUNTRY);
When(@"I click the text 'Country: " + executingCountry + "'");

// Select the country


When(@"I click the text '" + myCountry + "'");

if (ConfigurationManager.Instance.TargetIsIOS())
{
Then(@"I should wait for 3 seconds");
string query = QueryBuilder.XPath().Equals().Text(myCountry).Build();
if (Element.Exists(query, DynamicRecognition.NATIVE))
{
When(@"I click the text '" + myCountry + "'");
}
}
}

[Given(@"I log in with account")]


public void GivenILogInWithAccount()
{
When(@"I am on screen 'UserRegistration'");
//When(@"I select 'Germany' as my country");
When(@"I press the button 'Philips account'");
Then(@"I should wait for 3 seconds");
When(@"I enter text 'deku@mailinator.com' in the field 'Your email or
phone number'");
When(@"I enter text 'password123' in the field 'Enter password'");
When(@"I click the text 'deku@mailinator.com'");
When(@"I press the button 'Log In'");
Then(@"I should wait for UserRegistration to login");
Then(@"I should wait for 3 seconds");
When(@"I press the button 'I accept the Terms and Conditions'");
When(@"I press the button 'Continue'");
Then(@"I should wait for 10 seconds");
if (ConfigurationManager.Instance.TargetIsIOS())
{
String textValue = "API call timed out after 10 seconds";
string query = QueryBuilder.XPath().Equals().Text(textValue).Build();
while (Element.Exists(query, DynamicRecognition.NATIVE))

26
{
When(@"I press the button 'Continue'");
Then(@"I should wait for 10 seconds");
}
Then(@"I should wait for 5 seconds");
}
Then(@"I should go to the next screen");
}
[Given(@"Creating a new account")]
public void NewUserRegistration(string email)
{
When(@"I am on screen 'UserRegistration'");
When(@"I press the button 'Create my Philips account'");
When(@"I enter text 'test' in the field
'usr_createScreen_firstName_textField'");
When(@"I enter text "+email+"in the field
'usr_createscreen_emailormobile_textfield'");
When(@"I enter text 'password123' in the field
'usr_createScreen_password_textField'");
When(@"I press the button 'Accepting Apps Terms and Conditions'");
When(@"I scroll 'Down'");
Then(@"I should see the button 'Create my Philips account'");
When(@"I press the button 'Create my Philips account'");
Then(@"I should go to the next screen");
Then(@"I should see the text 'Confirm your email'");

}
public void NewUserRegistrationUnhappyFLow(String mail)
{
When(@"I am on screen 'UserRegistration'");
When(@"I press the button 'Create my Philips account'");
When(@"I enter text 'test' in the field
'usr_createScreen_firstName_textField'");
When(@"I enter text "+mail+ " in the field
'usr_createscreen_emailormobile_textfield'");
When(@"I enter text 'password123' in the field
'usr_createScreen_password_textField'");
When(@"I press the button 'Accepting Apps Terms and Conditions'");
When(@"I scroll 'Down'");
Then(@"I should see the button 'Create my Philips account'");
When(@"I press the button 'Create my Philips account'");
Then(@"I should go to the next screen");
Then(@"I should see the text 'Confirm your email'");
Then(@"I should see the text 'Your Email is already registered");

}
}
}

4.4 AppLauncher.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using PowerSleep_TestAutomation.Util;
using TechTalk.SpecFlow;

27
namespace PowerSleep_TestAutomation.Framework
{
public class AppLauncher
{
private static bool clearAppData = false;

public AppLauncher() { }

public static void ClearAppData(bool clear)


{
clearAppData = clear;
}

public static void Launch()


{
if (ConfigurationManager.Instance.TargetIsAndroid())
{
LaunchAndroid(clearAppData, true , true);
}
if (ConfigurationManager.Instance.TargetIsIOS())
{
LaunchIOS(clearAppData, true, true);
}
}

public static void Relaunch()


{
if (ConfigurationManager.Instance.TargetIsAndroid())
{
LaunchAndroid(false, true, false);
}
if (ConfigurationManager.Instance.TargetIsIOS())
{
LaunchIOS(false, true, false);
}
}

public static void Close()


{
if (ConfigurationManager.Instance.TargetIsAndroid())
{
CloseAndroid();
}
if (ConfigurationManager.Instance.TargetIsIOS())
{
CloseIOS();
}
}

private static void LaunchAndroid(bool doClearAppData, bool instrument, bool


stopIfRunning)
{
var client = SeeTestClientManager.GetClient();
if (doClearAppData)
{
string packageName =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_PACKAGE);
client.ApplicationClearData(packageName);
}

28
string package =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_PACKAGE);
string activity =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_LAUNCH_ACTIVITY);
string activityURL = package + "/" + activity;
client.DeviceAction("Home");
client.Launch(activityURL,true, stopIfRunning);
}

private static void CloseAndroid()


{
var client = SeeTestClientManager.GetClient();
string package =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_PACKAGE);
string activity =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_LAUNCH_ACTIVITY);
string activityURL = package + "/" + activity;
client.ApplicationClose(activityURL);
}

private static void LaunchIOS(bool doClearAppData, bool instrument, bool


stopIfRunning)
{
var client = SeeTestClientManager.GetClient();
string package =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_PACKAGE);
if (doClearAppData)
{
client.Uninstall(package);

client.Install(package, true);
}

client.Launch(package, instrument, stopIfRunning);


}

private static void CloseIOS()


{
var client = SeeTestClientManager.GetClient();
string package =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_PACKAGE);

client.ApplicationClose(package);
}

public static void InstallFromDisk()


{
string package =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_APP_PACKAGE);
string appPath = "";

if (ConfigurationManager.Instance.TargetIsAndroid())
{
appPath =
ConfigurationManager.Instance.GetRepositoryPathFor(@"Scripts\HSS.apk");

29
}
if (ConfigurationManager.Instance.TargetIsIOS())
{
appPath =
ConfigurationManager.Instance.GetRepositoryPathFor(@"Scripts\HSS.ipa");
}

var client = SeeTestClientManager.GetClient();

// first uninstall the app package from phone


client.Uninstall(package);

// then install from disk instead of seetest repo


client.Install(appPath, true);
}
}
}

4.5 ConfigurationManager.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

using PowerSleep_TestAutomation.Framework.Cloud;

namespace PowerSleep_TestAutomation.Framework
{
public class ConfigurationManager
{
private static string HSS_EXT_CONFIG_DIR = "HSS_EXT_CONFIG_DIR";
private static string HSS_TARGET_PLATFORM = "HSS_TARGET_PLATFORM";

private static string HSS_TARGET_ANDROID = "android";


private static string HSS_TARGET_IOS = "ios";

public static string HSS_APPFLOW_FILENAME = "HSS_AppFlow.json";


public static string HSS_TOOLS_DIRNAME = "Tools";
public static string HSS_CONFIG_DIRNAME = "Configuration";
public static string HSS_SESSIONS_DIRNAME = "Sessions";
public static string HSS_SEETEST_OBJECT_REPOSITORY_DIRNAME =
@"ObjectRepository\HSS";
public static string HSS_SEETEST_REPORT_DIRNAME = @"Reports";
public static string HSS_SCREENSHOT_DIRNAME = "Screenshots";

#if _IOS_DEFAULT_PLATFORM
private static string HSS_DEFAULT_PLATFORM = "ios";
#else
private static string HSS_DEFAULT_PLATFORM = "android";
#endif

#region ConfigurationProperties
public static string ST_CLIENT_HOST = "st_client_host";
public static string ST_CLIENT_PORT = "st_client_port";
public static string ST_CLIENT_USE_SESSION_ID = "st_client_use_session_id";

public static string HSS_ROOT_REPO_PATH = "hss_root_repo_path";

30
public static string HSS_APP_PACKAGE = "hss_app_package";
public static string HSS_APP_LAUNCH_ACTIVITY = "hss_app_launch_activity";
public static string HSS_TARGET_DEVICE = "hss_target_device";
public static string HSS_EXECUTING_COUNTRY = "hss_executing_country";

public static string HSS_POWERSLEEP_DEVICES = "powersleep_devices";


public static string HSS_POWERSLEEP_DEVICE_ID = "powersleep_device_id";
public static string HSS_POWERSLEEP_DEVICE_MAC = "powersleep_device_mac";

public static string HSS_USER = "hss_user";


public static string HSS_ACCOUNTS = "accounts";
public static string HSS_ACCOUNT_MAIL = "mail";
public static string HSS_ACCOUNT_PASSWORD = "password";
public static string HSS_ACCOUNT_UUID = "UUID";
#endregion

#region SingletonPattern
private static ConfigurationManager instance;

private ConfigurationManager()
{
Init();
}

public static ConfigurationManager Instance


{
get
{
if (instance == null)
{
instance = new ConfigurationManager();
}
return instance;
}
}
#endregion

private JObject configObject = null;

private bool app_installed_ios = false;


private bool app_installed_android = false;

private void Init()


{
// load configuration json file
string mainConfigFilePath =
GetExternalConfigurationFilePath("mainConfig.json");
using (StreamReader reader = new StreamReader(mainConfigFilePath))
{
string json = reader.ReadToEnd();
this.configObject = (JObject)JObject.Parse(json);
}
}

private static string GetTargetPlatform()


{
string platform = Environment.GetEnvironmentVariable(HSS_TARGET_PLATFORM);
if (String.IsNullOrEmpty(platform))
{
platform = HSS_DEFAULT_PLATFORM;
}

31
return platform;
}

private static string GetExternalConfigurationDirectory()


{
string configDir = Environment.GetEnvironmentVariable(HSS_EXT_CONFIG_DIR);
if (String.IsNullOrEmpty(configDir))
{
// use My Documents as system default
configDir =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
}
return configDir;
}

public static string GetExternalConfigurationFilePath(string fileName)


{
return Path.Combine(GetExternalConfigurationDirectory(), fileName);
}

public string GetExternalConfigurationPathFor(string configurationKey)


{
string configuredFilename =
GetStringConfigurationProperty(configurationKey);
return GetExternalConfigurationFilePath(configuredFilename);
}

public string GetRepositoryPathFor(string filePath)


{
return Path.Combine(GetRepositoryRootPath(), filePath);
}

public string GetRepositoryRootPath()


{
return GetStringConfigurationProperty(HSS_ROOT_REPO_PATH);
}

public string GetConfigurationPath()


{
return Path.Combine(GetRepositoryRootPath(), HSS_CONFIG_DIRNAME);
}

public string GetRepositoryConfigurationPathFor(string configKey)


{
return Path.Combine(GetConfigurationPath(), configKey);
}

public string GetSessionDirectory()


{
return GetRepositoryPathFor(HSS_SESSIONS_DIRNAME);
}

private JToken GetConfigurationProperty(JToken fromObject, string propertyKey)


{
if (fromObject != null)
{
return fromObject[propertyKey];
}
else
return null;
}

32
public string GetStringConfigurationProperty(string propertyKey)
{
return (string)GetConfigurationProperty(this.configObject, propertyKey);
}

public int GetIntConfigurationProperty(string propertyKey)


{
return (int)GetConfigurationProperty(this.configObject, propertyKey);
}

public bool GetBoolConfigurationProperty(string propertyKey)


{
return (bool)GetConfigurationProperty(this.configObject, propertyKey);
}

public string GetStringConfigurationPropertyForTargetPlatform(string


propertyKey)
{
string platform = GetTargetPlatform().ToLower();
JToken platformObject = GetConfigurationProperty(this.configObject,
platform);
return (string)GetConfigurationProperty(platformObject, propertyKey);
}

public bool TargetIsAndroid()


{
return HSS_TARGET_ANDROID.Equals(GetTargetPlatform().ToLower());
}

public bool TargetIsIOS()


{
return HSS_TARGET_IOS.Equals(GetTargetPlatform().ToLower());
}

public PowerSleepDevice GetPowerSleepDevice(string id)


{
JToken powersleepDevices = this.configObject[HSS_POWERSLEEP_DEVICES];
JToken device = powersleepDevices[id];
if (device != null)
{
return new PowerSleepDevice(id, device);
}
else
return null;
}

public List<string> GetPowerSleepDeviceIDs()


{
JObject powersleepDevices =
(JObject)this.configObject[HSS_POWERSLEEP_DEVICES];
List<string> deviceIds = new List<string>();
foreach (JProperty device in powersleepDevices.Properties())
{
deviceIds.Add(device.Name);
}
return deviceIds;
}

public List<User> GetUsers()


{
JArray accountArray = (JArray)this.configObject[HSS_ACCOUNTS];
List<User> users = new List<User>();

33
foreach (JToken accountToken in accountArray)
{
User user = new User(accountToken);
users.Add(user);
}
return users;
}

public User GetUserByMail(string userMail)


{
List<User> users = this.GetUsers();
foreach (User user in users)
{
if (user.Mail.Equals(userMail))
{
return user;
}
}
return null;
}

public bool AppInstalled


{
get
{
if (TargetIsAndroid())
{
return app_installed_android;
}
if (TargetIsIOS())
{
return app_installed_ios;
}
return false;
}
set
{
if (TargetIsAndroid())
{
app_installed_android = value;
}
if (TargetIsIOS())
{
app_installed_ios = value;
}
}
}
}
}

4.6 Hooks.cs
using System;
using PowerSleep_TestAutomation.Framework.SeeTest;
using TechTalk.SpecFlow;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.IO;

namespace PowerSleep_TestAutomation.Framework
{
[Binding]

34
public class Hooks
{
[BeforeScenario]
public void BeforeScenario()
{
// Check if we are running platform specific scenario on wrong
configuration
DoPlatformAndBuildConfigMatch();

// Power off all powersleep devices


foreach (string deviceId in
ConfigurationManager.Instance.GetPowerSleepDeviceIDs())
{

ConfigurationManager.Instance.GetPowerSleepDevice(deviceId).PowerOff();
}

SeeTestClientManager.Instance.CreateClient();
}

[AfterScenario]
public void AfterScenario()
{
if (false & ScenarioContext.Current.TestError != null) // deactivated
{
// Test failed, take screenshot
SeeTestClientManager.Instance.Client.Capture();

// Find out the file path to the screenshot


Dictionary<string, object> resultMap =
SeeTestClientManager.Instance.Client.getLastCommandResultMap();
string screenshotPath = (string)resultMap[@"outFile"];

// Create filename
string featureTitle = FeatureContext.Current.FeatureInfo.Title;
string scenarioTitle = ScenarioContext.Current.ScenarioInfo.Title;
string time = DateTime.Now.ToString("s");
string fileName = String.Format("{0}_{1}_{2}.png", featureTitle,
scenarioTitle, time);

// Prepare output location


string targetDir =
ConfigurationManager.Instance.GetRepositoryPathFor(ConfigurationManager.HSS_SCREENSHOT
_DIRNAME);
if (!Directory.Exists(targetDir))
{
Directory.CreateDirectory(targetDir);
}

// Copy the screenshot to safe place


File.Copy(screenshotPath, Path.Combine(targetDir, fileName), true);
}
SeeTestClientManager.Instance.FinishClient();
}

[AfterStep]
public void AfterStep()
{
// check if SeeTest Internal Exception occurred, mark as inconclusive
result
Exception exception = ScenarioContext.Current.TestError;
if (ErrorAnalysis.IsSeeTestError(exception))

35
{
Assert.Inconclusive(exception.Message);
}
}

private void DoPlatformAndBuildConfigMatch()


{
// check if tags contain either iOS or Android
foreach (string tag in ScenarioContext.Current.ScenarioInfo.Tags)
{
if (tag.ToLower().Equals("ios"))
{
if (ConfigurationManager.Instance.TargetIsAndroid())
{
// Stop test
ScenarioContext.Current.Pending();
}
}
if (tag.ToLower().Equals("android"))
{
if (ConfigurationManager.Instance.TargetIsIOS())
{
// Stop test
ScenarioContext.Current.Pending();
}
}
}
}
}
}

4.7 PowerSleepDevice.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using PowerSleep_TestAutomation.Util;
using System.Threading;

namespace PowerSleep_TestAutomation.Framework
{
public class PowerSleepDevice
{
private string id = null;
private string mac = null;
private string usbPort = null;
private string ios_id = null;
private string comPort = null;
private string name = null;

public PowerSleepDevice(string id, string mac, string ios_id, string usbPort,


string comPort, string name)
{
this.id = id;
this.mac = mac;
this.ios_id = ios_id;
this.usbPort = usbPort;
this.comPort = comPort;

36
this.name = name;
}

public PowerSleepDevice(string id, JToken json) : this(id,


(string)json["mac"], (string)json["ios_id"], (string)json["usb_port"],
(string)json["com_port"], (string)json["name"]) { }

public string ID
{
get { return id; }
}

public string MACAddress


{
get { return mac; }
}

public string USBPort


{
get { return usbPort; }
}

public string IOS_ID


{
get { return ios_id; }
}

public string COMPort


{
get { return comPort; }
}

public string Name


{
get { return name; }
}

public void PowerOn()


{
if (!String.IsNullOrEmpty(usbPort))
{
YKUSHControl.EnablePort(int.Parse(this.usbPort));
}
}

public void PowerOff()


{
if (!String.IsNullOrEmpty(usbPort))
{
YKUSHControl.DisablePort(int.Parse(this.usbPort));
}
}

public void Reboot()


{
PowerOff();
Thread.Sleep(3000);
PowerOn();
Thread.Sleep(5000);
}

public void FactoryReset()

37
{
if (!String.IsNullOrEmpty(comPort))
{
PowerSleep.DeviceControl deviceControl =
PowerSleep.DeviceControl.Instance;
deviceControl.Open(comPort);
deviceControl.FactoryReset();
deviceControl.Close();
}
}

public void AddSession(PowerSleep.Session session)


{
if (!String.IsNullOrEmpty(comPort))
{
PowerSleep.DeviceControl deviceControl =
PowerSleep.DeviceControl.Instance;
deviceControl.Open(comPort);

// Send Session
deviceControl.AddSession(session);

deviceControl.Close();
}
}

public string GetPlatformSpecificID()


{
if (ConfigurationManager.Instance.TargetIsAndroid())
{
return MACAddress;
}
else if (ConfigurationManager.Instance.TargetIsIOS())
{
return IOS_ID;
}
return null;
}
}
}

4.8 ScenarioContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PowerSleep_TestAutomation.Framework
{
public class ScenarioContextKeys
{
public static string CURRENT_SCREEN = "tfw_current_screen";
}
}

SeeTestClientManager.cs

using System;
using System.Collections.Generic;
using System.Linq;

38
using System.Text;
using System.Threading.Tasks;

using experitestClient;

namespace PowerSleep_TestAutomation.Framework
{
public class SeeTestClientManager
{
#region SingletonPattern
private static SeeTestClientManager instance;

private SeeTestClientManager()
{
}

public static SeeTestClientManager Instance


{
get
{
if (instance == null)
{
instance = new SeeTestClientManager();
}
return instance;
}
}
#endregion

private Client currentClient = null;

public void CreateClient()


{
string host =
ConfigurationManager.Instance.GetStringConfigurationProperty(ConfigurationManager.ST_C
LIENT_HOST);
int port =
ConfigurationManager.Instance.GetIntConfigurationProperty(ConfigurationManager.ST_CLIE
NT_PORT);
bool useSessionID =
ConfigurationManager.Instance.GetBoolConfigurationProperty(ConfigurationManager.ST_CLI
ENT_USE_SESSION_ID);
string deviceId =
ConfigurationManager.Instance.GetStringConfigurationPropertyForTargetPlatform(Configur
ationManager.HSS_TARGET_DEVICE);
currentClient = new Client(host, port, useSessionID);

currentClient.SetProjectBaseDirectory(ConfigurationManager.Instance.GetRepositoryPathF
or(ConfigurationManager.HSS_SEETEST_OBJECT_REPOSITORY_DIRNAME));
currentClient.SetReporter("xml",
ConfigurationManager.Instance.GetRepositoryPathFor(ConfigurationManager.HSS_SEETEST_RE
PORT_DIRNAME), "HSS_TestAutomation");
currentClient.SetShowImageInReport(false);
currentClient.SetDevice(deviceId);
if (ConfigurationManager.Instance.TargetIsIOS())
{
currentClient.SetProperty("ios.elementsendtext.action.fire", "true");
}
}

public void FinishClient()


{

39
// Generates a report of the test case.
// For more information -
// https://docs.experitest.com/display/public/SA/Report+Of+Executed+Test
currentClient.GenerateReport(false);

// close current app

currentClient.ApplicationClose(ConfigurationManager.Instance.GetStringConfigurationPro
pertyForTargetPlatform(ConfigurationManager.HSS_APP_PACKAGE));

// Releases the client so that other clients can approach the agent in
// the near future.
currentClient.ReleaseClient();
}

public Client Client


{
get
{
return currentClient;
}
}

public static Client GetClient()


{
return Instance.Client;
}
}
}

4.9 ScreenFlow.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json.Linq;

namespace PowerSleep_TestAutomation.Framework
{
public class ScreenFlow
{
private class ScreenFlowEntry
{
public ScreenFlowEntry(string target, string previous, string step)
{
this.Target = target;
this.Previous = previous;
this.StepDefinition = step;
}

public string Target { get; set; }

public string Previous { get; set; }

public string StepDefinition { get; set; }

40
}

private List<ScreenFlowEntry> entries = null;

#region SingletonPattern
private static ScreenFlow instance;

private ScreenFlow()
{
LoadAppFlow();
}

public static ScreenFlow Instance


{
get
{
if (instance == null)
{
instance = new ScreenFlow();
}
return instance;
}
}
#endregion

private void LoadAppFlow()


{
string appFlowFilePath =
ConfigurationManager.Instance.GetRepositoryConfigurationPathFor(ConfigurationManager.H
SS_APPFLOW_FILENAME);
using (StreamReader reader = new StreamReader(appFlowFilePath))
{
string json = reader.ReadToEnd();
JArray entryArray = (JArray.Parse(json));
this.entries = new List<ScreenFlowEntry>();

foreach (var entry in entryArray)


{
string targetName = (string)entry["targetScreen"];
string previous = (string)entry["previousScreen"];
string step = (string)entry["stepDefinition"];
entries.Add(new ScreenFlowEntry(targetName, previous, step));
}

}
}

public IList<string> GetStepsForTarget(string flowTarget)


{
ScreenFlowEntry targetEntry = GetEntryForTarget(flowTarget);
if (targetEntry != null)
{
// build list of steps
List<string> steps = new List<string>();
steps.Add(targetEntry.StepDefinition);

// get next step


ScreenFlowEntry previousEntry =
GetEntryForTarget(targetEntry.Previous);
while (previousEntry != null)
{
steps.Add(previousEntry.StepDefinition);

41
previousEntry = GetEntryForTarget(previousEntry.Previous);
}

// reverse flow
steps.Reverse();
return steps;
}
return null;
}

private ScreenFlowEntry GetEntryForTarget(string flowTarget)


{
foreach (ScreenFlowEntry entry in entries)
{
if (entry.Target.Equals(flowTarget))
{
return entry;
}
}
return null;
}
}
}

5 . RESULTS & DISCUSSIONS :

All the automation scripts are up and running and if there is any flaw in any build the execution
of the scripts fails and would generate the report in Jenkins logs. With this we can track whether
the error which has occurred is due to a bug or script failure and can be fixed easily. The
execution of the scripts is done on a nightly basis by the Jenkins server.

6 . CONCLUSION & FUTURE WORK :

Using SeeTest automation we were able to create an automation framework and have increased
the test coverage and speed by automating the test scripts. Test automation decreases the need
for manual testing and improves the coverage of test cases. The future work would be
maintaining and fixing the existing test scripts and automate any new test cases for the features
which come out in the later releases.

42

You might also like