Independent Study Summary

 

When I left SAP in October 2016, having worked for 2.5 years as an Associate Software Developer in India, the company was still defining its strategy to be “the Cloud company”. Today, SAP is one of the fastest growing Cloud-based software vendors, with revenues from Cloud-based software and services outpacing that of the traditional On-premise software and services. However, the highly mature, internal software landscape is still in the process of transition from an On-premise to a Hybrid landscape (i.e. On-premise integrated with Cloud). This made it a perfect place for my independent studies as I got to experience the process, the challenges, and the risks involved in Cloud system implementation and testing. During the 7 months of Internship with the Go-To-Market Services team at SAP, I contributed towards transitioning the internal Deal Management system wherein I got an opportunity to apply the Software Design skills (like Design Principles and Patterns), and Software Development skills (like latest DevOps concepts -automated testing, continuous integration and continuous deployment, version control etc.) that I gained during my graduate studies at the University.

The first month of the internship was particularly tough for me. Having been a Backend Developer in the past with expertise in SAP proprietary technologies and frameworks, I had little experience working with currently used open source frameworks based on Python and JavaScript(JS). Neither did I have much exposure to Frontend/User Interface (UI) development and testing.  To be able to contribute meaningfully, not only did I have to quickly gain these necessary technical skills, but I also had to interact with my colleagues with their busy schedules, attend training and read a lot of documents to understand the functionality of the system that the team was working on. I had to work extra, even after office hours, to get up to speed.

My first official task was to prepare a Proof of Concept (PoC) for test automation of Callidus Cloud Configure Price Quote (CPQ) system that we were implementing for Sales Quotation management. Since SAP had recently acquired the software, the team lacked expertise in this area. After successful POC with Selenium-based Protractor end-to-end UI testing, JS framework, I went on to create automation test scripts to cover all core functionalities of the CPQ system. Since the project followed an agile development model, the automation of test cases helped catch bugs early with each deployment and allowed the team to fix the issues with the least business impact. Having proven my capability, I was included in core development. Initially, I could implement small requirements and bug-fixes. Gradually, I progressed onto implementing more complex functionalities and reached a stage where the team trusted me with the development of some modules entirely by myself. Using my coding skills and Python programming proficiency, I developed an API for exposing Quote information in CPQ system to mobile applications for iOS and Android platforms. I also refactored a few modules and optimized the system performance for some scenarios. Apart from these tasks, I supported several system deployments during weekends and proactively performed bug-fixes. My contribution to the team was greatly appreciated by my Manager by awarding me “Keep the promise” award 3 times during the internship.

Although my internship was very satisfactory in terms of learning, there are three things I think I should have done differently. Firstly, I should have reflected on my work on a regular basis and note down what went right and what I need to focus more on. This self-introspection could have helped me plan and manage better. Secondly, I should have invested more time into understanding the business processes thoroughly and gain a broader perspective of the business domain rather than just concentrating more towards providing technical solutions. Deep domain knowledge would have been a valuable skill in my repertoire, making me a well-rounded professional not restricted by technology. Thirdly, I think I should have tried exploring the application of data science like the implementation of Machine Learning algorithms at my work. This would have given me much-needed experience for applying data science concepts in solving real-world problems.

In conclusion, I believe that I have learned many new skills at my internship. My understanding of the Cloud-software architecture, On-premise and Cloud-software integration, and Cloud-system design and development has increased greatly. Further, I have gained knowledge of various open source technologies and cultivated skills of writing production standard Python and JavaScript code, thereby supplementing my past SAP proprietary technology expertise.  In addition, I learned valuable inter-personal skills by working with a team comprising of members from diverse international backgrounds. All of this has given me a lot of confidence to pursue my career ambitions as I plan to graduate from the University.

Part 5 – HANA Database Schema

Relational databases contain a catalog that describes the various elements in the system. The catalog divides the database into sub-databases known as a schema. A database schema enables you to logically group together objects such as tables, views, and stored procedures1. Schemas are also one of the most important levels on which user Authorizations can be defined in HANA.

The first step in creating a HANA Persistence Model is to define a HANA Database Schema.

A HANA database schema can be easily created as described below

  1. Select “Editor” view of SAP HANA Web-based Development Workbench

1

 

2. Right on a Package (ex. demo) > select New > select File

2

 

3. Provide a filename with extension .hdbschema > click Create

3

 

4. Type below code > Click Save

4

5. Open “Catalog“ view of SAP HANA Web-based Development Workbench

5

 

6. Authorization needs to be granted to User to be able to view the newly created system. This can be done by executing a procedure in SQL console of HANA catalog.

6

 

7. Grant SELECT authorization on a schema by executing below code call  _SYS_REPO.GRANT_SCHEMA_PRIVILEGE_ON_ACTIVATED_CONTENT(‘select’,'<SCHEMANAME>’,'<username>’);

7

The newly created schema is now visible in Catalog.

8

 

Reference:

  1. SAP HANA HDBTable Syntax Reference

https://help.sap.com/viewer/cc2b23beaa3344aebffa2f6e717df049/2.0.02/en-US/6cdea002ce634a8c81181de92bc0d8ea.html

 

 

 

 

Part 4 – Introduction to SAP HANA Core Data Services (CDS)

SAP HANA is a SQL compliant database i.e. it supports standard Data Definition Language (DDL), Data Control Language(DCL), Transaction Control Language (TCL), and Data Manipulation Language constructs of SQL. However, it also provides a native infrastructure called Core Data Services (CDS) for defining and consuming semantically rich data models. Using a data definition language (DDL), a query language (QL), and an expression language (EL), CDS enables write operations, transaction semantics, and more.

CDS specification allows the definition of the following artifacts and elements:

  • Entities (tables)
  • Views
  • User-defined data types (including structured types)
  • Contexts
  • Associations
  • Annotations

CDS artifacts are design-time definitions that are stored in the HANA repository. They generate corresponding run-time objects when they are activated. The CDS document containing the design-time definitions that you create using the CDS-compliant syntax must have the file extension .hdbdd, for example, MyCDSTable.hdbdd.

Some of the advantages of using CDS are

  • Easy deployment of database catalog objects like tables, views etc. between various systems of the landscape especially using SAP’s ABAP Transport Systems
  • Version management benefits provided by HANA repository are extended to database catalog objects
  • Semantic information can be expressed in the form of annotations at the time of definition

 

References:

  1. SAP HANA Core Data Services (CDS) Reference – https://help.sap.com/viewer/09b6623836854766b682356393c6c416/1.0.12/en-US/b523afd66f5a40469573d9c47d7af831.html

Part 3 – SAP HANA Web-Based Development Workbench

SAP HANA Web-Based Development Workbench is a browser-based IDE that can be used to build and test development artifacts in an SAP HANA Database. It provides intuitive User Interface (UI) and convenient functions which simplify the development of native HANA applications in a Web browser without having to install any development tool. For example, it provides wizards for creating applications based on templates which eliminate a lot of boilerplate coding.

The SAP HANA Web-based Development Workbench is available on the SAP HANA XS Web server at the following URL:

http://<WebServerHost&gt;:80<SAPHANAinstance>/sap/hana/ide

h3

 

SAP HANA Database objects like Schemas, Tables, Views, Procedures etc. are referred to as run-time objects. These are the objects which are physically stored in the Database. Each of these objects has a corresponding design-time object which is nothing but a file in the HANA repository with a specific extension. When a design-time object is compiled (called Activation in HANA terminology), run-time objects are generated in the database. HANA repository enables version control, deployment, and concurrent development by teams. Although it is possible to generate HANA run-time objects using SQL DDL statements, the recommended best practice is to generate them by activating repository design time objects to take advantage of the Repository features.

Editor

It is the UI which enables development, maintenance, and execution of design-time objects in the HANA Repository. Some of its features are

  1. Automatic syntax highlighting and code completion
  2. Version management – Git like capabilities
  3. Execution and Debugging of objects
  4. Template wizards – generate boilerplate for common types of HANA native applications

 

h30

 

Catalog

It is the UI which enables development, maintenance, and execution of SQL Catalog objects of SAP HANA Database, which are the run-time objects generated by activation of design-time objects in the Editor. It also provides a full-fledged SQL console which enables execution any kind of SQL statements DDL, DML, and DCL statements. The objects are divided into schemas, which is a way to organize activated database objects. Catalog also provides an interface for data provisioning from remote data sources.

 

h31

 

Security

It is the UI for DB Authorization Management. It enables the creation and maintenance of Database Users and Authorization Roles. It also allows assignment of Roles to Users.

 

h32

 

Traces

It is the UI for management for HANA traces. It enables configuring, viewing and deleting traces.

h33

 

References:

SAP HANA Developer Guide https://help.sap.com/viewer/b3d0daf2a98e49ada00bf31b7ca7a42e/2.0.03/en-US/7f99b0f952d04792912587c99e299ef5.html

Introduction to OOPs in Python

What Is Object-Oriented Programming (OOP)?

Object-oriented Programming, or OOP, in short, is a programming paradigm that helps in structuring programs so that the properties and behaviors are bundled into individual objects. For instance, an object could represent a person with properties such as age, name, address, etc., with behaviors such as walking, talking and running.

Class:

Classes provide a means of bundling data and functionality together by defining blueprint/skeleton for the object.

Syntax:

a2

Object:

An object is an instantiation of a class. When class is defined, only the description for the object is defined. But when an object is created then memory or storage is allocated is also allocated.

Syntax:

a1

Class and Object Example:

a3

The above example has a class name ‘Apple’, with three different components.

1.      Class Attributes: These are attributes that are shared across all the objects.

2.      Instance Attributes: These are attributes that hold the properties of the objects. They are different for different objects.

3.      Methods: These hold the logic that defines the behavior of an object.

The above example also creates two objects, app1 and app2 having a respective name and color property. Separate memory gets allocated to two of the objects.

 

Inheritance:

Inheritance enables new objects to get the properties of existing objects. A class that is used as the base for inheritance is called a superclass or base class. A class that inherits from a superclass is called a subclass or derived class.

 

a7

output:

a6

 

In the above example, ‘Fruit’ is the base class and ‘Apple’ is the child class. Even though a color method is not present in the Apple class, it gets inherited from the base class Fruit.

Note: super is the keyword used to call parent class constructor in python. When the apple object is initialized, first the parent constructor is called and then the child constructor gets called.

 

Encapsulation:

Encapsulation is an Object-Oriented Programming concept that binds together the data and functions that applies to the data, and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.

25

Output:

a4

In the above example, size is a private property which can be modified only by the methods within that class. When the variable is tried to update the value outside of the class then action has no effect and value remains unchanged.

 

 

 

 

 

 

 

Part 2 – SAP HANA System Architecture Overview

An individual installation, called the SAP HANA System, comprises of the following components:

  1. Multiple isolated databases which include a mandatory System Database and one or more optional Tenant Databases and
  2. A Platform for running SAP HANA-based Web applications called the SAP HANA XS Advanced (XSA) application server

This System can either be installed on a single physical host or distributed across a cluster of several hosts (referred as Multi-Host System, or scale-out system) for reasons of scalability and availability. The same is true for each of the above-mentioned components.

The following figure shows a sample system with three databases (system database and three tenant databases) on a single host.1

temp.png

 

System Database2

Every SAP HANA System contains exactly one System Database. It is not a full-featured SAP HANA database and does not provide full SQL support. It is intended to be used for central system administration. So, it contains overall landscape information of the System as a whole, including the knowledge of the tenant databases that exist in the system. However, it does not own database-related topology information, that is, information about the location of tables and table partitions in the tenant databases. Administration tasks performed in the system database may apply to the entire System with all its databases (for example, system-level configuration settings), or can target specific tenant databases (for example, backup of a tenant database).

Some administration tasks performed in the System Database3 are

  • Starting and stopping the entire system
  • Monitoring the system
  • Configuring parameters in configuration (*.ini) files at the system level
  • Setting up and configuring tenant databases
  • Backing up tenant databases
  • Recovering tenant databases

 

Tenant Databases4

An SAP HANA System can have zero or more isolated databases called Tenant Databases. They share the same installation of database system software, the same computing resources (memory and CPU Cores), and the same system administration. However, each database is self-contained and fully isolated with its own:

  • Set of database users
  • Database catalog
  • Repository
  • Persistence (data)
  • Backups
  • Traces and logs

Some administration tasks that can be performed in the individual Tenant databases3 are

  • Monitoring the database
  • Provisioning database users
  • Creating and deleting schemas, tables, and indexes in the database
  • Backing up the database
  • Configuring database-specific parameters in configuration (*.ini) files

The main advantages of Multi-tenancy5 are

  1. Running multiple tenant databases on one SAP HANA System lowers capital expenditure as system resources can be better utilized
  2. Maintaining multiple tenant databases as a single entity reduces operating expenditure as database maintenance is simplified
  3. Development and deployment of secure, multi-tenant cloud-based applications are

SAP HANA XS Advanced (XSA)6

SAP HANA XS advanced application server makes it possible to develop and execute micro-service-oriented Web applications natively in an SAP HANA System. It provides a comprehensive platform on top of the HANA databases with a rich set of embedded services that enable end-to-end support for web-based applications including applications including lightweight web servers, persistence services, and a configurable identity provider. It makes the development of polyglot applications possible by providing a core set of pre-deployed runtimes that are accepted as the industry standard, for example, node.js or JavaEE. Having XSA as an integral part of the SAP HANA System provides an additional value proposition, as it allows development of native applications (traditional or cloud-based) without the need of any other hardware/software infrastructure.

 

References:

  1. SAP HANA Administration guide – Server Architecture of Tenant Databases

https://help.sap.com/viewer/6b94445c94ae495c83a19646e7c3fd56/2.0.03/en-US/f9aba40d6c4c4ae48cce461db4d42d88.html

  1. SAP HANA Administration guide – The System Database

https://help.sap.com/viewer/6b94445c94ae495c83a19646e7c3fd56/2.0.03/en-US/39da3d057f56427ab1bb7f738ca9e7ce.html

  1. SAP HANA Administration guide – Administration of Tenant Databases

https://help.sap.com/viewer/6b94445c94ae495c83a19646e7c3fd56/2.0.03/en-US/003b69b2805342d79d1afb94d61e13fd.html

  1. SAP HANA Administration guide – Tenant Databases

https://help.sap.com/viewer/6b94445c94ae495c83a19646e7c3fd56/2.0.03/en-US/623afd167e6b48bf956ebb7f2142f058.html

  1. SAP HANA Multitenant Database Containers

https://blogs.saphana.com/2015/01/27/sap-hana-multitenant-database-containers/

  1. SAP HANA Administration guide – Server Architecture of SAP HANA XS Advanced Runtime Platform

https://help.sap.com/viewer/6b94445c94ae495c83a19646e7c3fd56/2.0.03/en-US/5fb6e7a65b1d447fabeebd3d5051cf32.html

Exception Handling in Python

Introduction:

In the Internship, from past month, I am working with the team that develops APIs(python scripts) to expose Callidus Configure Price Quote(CPQ) data to Deal Approval mobile application used by Account Executives. These APIs were initially developed by other team and later it was taken over by my team for further developments. All the APIs are very huge, and which is causing us difficulty in maintaining it. Hence has decided to redesign the APIs, which are more readable and maintainable. Three of the team members are selected to redesign the APIs, and I am one of them. Hence to prepare myself for the upcoming tasks, I will be going through few topics that will help me in redesigning APIs.

Following are the list of topics I will go through in the coming days:

  • Python OO
  • Exception handling
  • Most used Design patterns
  • Design principles(SOLID)

 

 

Exception Handling

 

What is Exception?

An exception is an event that occurs during the execution of a program which disrupts the normal flow of execution of the program. An exception is a Python object that signifies an error. When a Python script raises an exception, it must either handle the exception handling or else the program will terminate abruptly.

Some of the common Exceptions:

Exception

Base class for all exceptions
ArithmeticError Errors related to numeric calculations
ZeroDivisionError Raised when the divisor is Zero in division
IOError Raised for input and output related errors
ValueError Raised when arguments have invalid data
RuntimeError Falls into this category when an error does not fall into another category
TypeError

Raised for the invalid data type

What is Exception handling?

Exception handling is the process of responding to exceptions when a program runs. It attempts to gracefully handle those situations so that a program does not end abruptly.

Python handles exception using try, except block.

Syntax:

2018-10-20_19-38-00

Try: Try block contains logic. Each try block can have multiple exception handlers. This becomes useful when the code inside try block can raise multiple exceptions.

Except: This block contains logic to handle the exception.

Else: If no exception is raised by try block, then logic inside else part gets executed.

Example:

2.png

Case 1:

a = 10

b = 0

 In this case, since the divisor is zero, an exception ‘ZeroDivisionError’ is raised, and “Divisor cannot be Zero” is printed.

Case 2:

a = 10

b = 2

In this case,  no exception is raised, else part is executed and “Success” is printed.

 

Finally clause:

Finally block is executed, irrespective of whether an exception is raised or not.

Example:

2018-10-20_19-40-29    Fileop.close() will be execute if exception is raised or not.

 

Raising an Exception:

An exception can be raised by using “raise” statement.

Example:

2018-10-20_19-40-40

Based on certain conditions exception will be raised. The important point here is that, whatever error is raised, except should be able to handle it.

 

User-Defined Exceptions:

Python allows creating your own exception by deriving classes from standard exceptions.

Example:

1

In the try block, the user-defined exception is raised and caught in the except block. In the exception block, “err” object of a user-defined exception is created.

 

 

 

Part 1: SAP HANA – Introduction

For 2.5 years of working at SAP in the past, the buzzword was SAP HANA. It was pitched as a promising product that had a potential to disrupt the traditional Relational Database (RDBMS) market and was supposed to be a game-changer in the Enterprise Software industry. So much so, that SAP decided to orient its entire strategy and product offerings around the technology. As a result, it became the fastest growing product in SAP’s history.

Unfortunately, back then I did not get an opportunity to work with the technology. However, as part of my job at the Go-to-market services team, I need to develop applications for the internal Customer Relationship Management (CRM) system whose backbone is SAP HANA. So, I am finally getting to see it in action and gain some valuable skills.

What is SAP HANA?

HANA stands for High-performance Analytical Appliance. At the core, SAP HANA is an ACID-compliant Relational Database Management System (RDBMS). However, it’s unique selling proposition is the groundbreaking performance, that it offers by combining Online Transaction Processing (OLTP) and Online Analytical Processing(OLAP) into a single In-memory database by eliminating disk bottlenecks, reducing data footprint with efficient data compression using columnar structure, and efficient parallel processing across multiprocessor cores. These technologies enable real-time/near real-time access to Enterprise data by eliminating predefined aggregates, materialized views, and data duplication between operational and decision support systems3.

SAP HANA provides a single-scalable platform to build next-generation Big Data, IoT, and advance analytics applications with native Machine Learning, Spatial Processing, Graph, Streaming analytics, Time series, Text analytics/search, and Cognitive services that can derive real-time actionable insights from a wide variety of data types2.

HANA

 

Some other noteworthy features of SAP HANA are:

  • Simplified Data Modeling3 – Complex business logic can be built into the database using graphical modeling tools, decision tables, core data services, and pre-built native function libraries. It allows push down of business logic code from the application layer to the database and reduces the impact of network bottlenecks by provides highly aggregated/processed results back to the application layer.
  • Multitenant Database3 – several databases containers that share the same memory and processors but securely isolated from one another, having its own users, catalog, repository, data, logs, and services can be managed as one. This reduces operating costs and simplifies database security and maintenance.
  • Multi-Tier Storage3 – its dynamic tiering functionality enables efficient memory usage by keeping frequently accessed data in memory and moving rarely accessed data to disk. This allows cost-effective management of large data volumes without being limited by memory size.
  • Multiple deployment options2 – a variety of deployment options that best fit the business needs from a developer learning the technology to performing a proof-of-concept, to running business-critical applications are available.
  • SAP HANA, express edition – deploys on laptop/desktop or in the Public Cloud for resource-constrained
  • SAP HANA, Cloud – Private Cloud, Managed Service or Public Cloud, Infrastructure-as-a-Service or Public Cloud, Pay-as-you-go model from different vendors.
  • SAP HANA, on-premise – quick to deploy appliance or with a tailored data center integration (TDI) model to leverage existing hardware and infrastructure on-premise.

References:

1 https://www.sap.com/products/hana/features/in-memory-database.html

2 https://blogs.saphana.com/2017/09/29/3-reasons-businesses-run-on-sap-hana/

3 https://www.sap.com/products/hana.html

Construct XPath in Selenium WebDriver

What is XPath and why it is needed?

It is a syntax for finding any element on the web page using XML path expression. It is used when elements cannot be found using common locators like id, class, name, etc.

Format of XPath: //tagname[@attribute=’value’]

 

Types of XPath:

  • Absolute Xpath:

It is the direct way to determine the element from the root node. The disadvantage of this method is if any element in the path is deleted or any element is added then path gets altered, and element won’t get identified.

  • Relative XPath:

This is the most commonly used way to build the XPath and identify the element. Xpath can be built from anywhere in HTML DOM, hence the size of the XPath is shorter and won’t get altered very frequently.

Some of the most commonly used functions to identify elements

Contains ():

Contains is a method used in constructing XPath expression. It is used when the element needs to be determined by its text or by partial text.

Ex:

2018-10-04_14-45-351

Element ‘Gmail’ can be determined using Contains method and using attribute as text by the below syntax

Xpath = “//a[contains(text(),’Gmail’)]”

AND or OR:

2018-10-04_15-15-582

Using AND expression, both the condition must be true to identify the element. Even if one of the condition fails then element won’t be determined.

To be able to recognize the element ‘Gmail’, both the condition has to be valid.

Ex: XPath = “//a[@class= ‘gb_P’ and @data-pid=’23’]”

Using OR expression, any of the condition can be true to identify the element. If both the condition fail then element won’t be determined.

Ex: XPath = “//a[@class= ‘gb_P’ or @data-pid=’23’]”

Starts-with:

The starting portion of the text of the attribute is used to determine the element whose attribute changes dynamically.

Determine ‘Gmail’ element using start-with function

Ex: Xpath=”//a[starts-with(text(),’Gma’)]”

Axes methods:

These methods are used to determine some of the complex, dynamic elements.

Following:

This method selects all the elements in the document of the current node.

2018-10-07_10-28-433

Ex: To select all the links after the ‘Selenium Downloads’ using ‘Following’.

XPath = ‘//a[contains(text(),’ Selenium Downloads’)]//following:: a’

Preceding:

Selects all nodes that come before the current node.

2018-10-07_10-28-434

 

Ex: If we need to select ’Selenium Downloads’ using ‘Previous Releases’

XPath = ‘//a[contains(text(),’ Previous Releases’)]//preceding:: a’

Child:

Selects all children elements of the current node.

2018-10-07_10-28-433

Ex: To select all the child of ’Selenium Downloads’

XPath = ‘//a[contains(text(),’ Selenium Downloads’)]//child:: a’

 

Protractor

What is protractor?

The protractor is an end to end testing framework for Angular JS based applications. It is not just for testing Angular JS application but also used for writing automated test cases for other web applications.

The protractor is a NodeJS program which along with Node identifies web element in a web application, and it also uses web driver to control browser with user actions.

 

Why use Protractor?

  • Simple syntax to write
  • No need for sleep or waits
  • Has multiple browser support
  • Can be run in multiple platforms
  • Ability to run in multiple browsers

 

Setup necessary to run Protractor

As protractor is a node.js program, install node.js

Install Selenium server and chrome driver if you want your test case to run in Chrome browser.

Since Vyper is built on top of the protractor, I only had to install Node and rest were already present.

 

Files that are necessary to run the test case

  1. The configuration file or conf file.

The conf file provides information about the location of the test case, which framework to use(Ex, Jasmine), which browser (Ex, Chrome). If the configuration is not defined, the default settings will be used.

  1. Spec file.

  This file has the code to identify the web element to interact with the application.

 

Commonly used functions in Protractor

  • Describe

Syntax: describe(‘text’, function()

Describe is a global function present in Jasmine which as two parameters, a string and a function. The string is used for describing the name for the test case and the function contains all the code that will implement the logic to execute the test case.

 

  • It

Syntax : it(‘text’, function()

It is a function present in Jasmine which as two parameters, a string and a function. The string is used for describing the name for the test step and the function contains the logic to execute the test step.

 

  • Expect

Syntax: expect(comparing value).not.toBe(comparedto)

               expect(comparing value).toBe(comparedto)

This function is called expectation. For each condition, there has to be an appropriate value to compare to complete an assertion. This is usually returned inside the function it().

 

  • beforeEach and afterEach

These functions are executed before or after every execution of function it(), and is executed even if the test step pass or not.

 

References:

 

https://www.protractortest.org/#/toc

https://medium.com/@bhageerathreddy/understanding-the-basics-of-jasmine-framework-effa5c75719b

https://www.guru99.com/protractor-testing.html