Javadoc

Click here to view javadocs.

Plugins

  • Cucumber Extension by Vimal: here

Community Examples

  • Speeding up Test Execution with Appium by Sai Krishna: here
  • Virender has created an example of parallel execution using TestNG. View project here
  • Using the ExtentReports TestNG listener in Selenium Page Object tests by Bas Dijkstra: here

Reviews

  • ExtentReports review by Richard Kieft (also shows simplifying logs with the @Rule annotation): here
  • Advance Selenium Reporting with Screenshots Part 2 by Mukesh Otwani: here
  • ExtentReports comparison between 2.03.1 & 2.04 by Rogelio "CAGS" Caga-anan Jr: here
  • Selenium HTML result reporting using ExtentReports Version 2 by Sunil Patro: here

Contributors

View this link for all active contributors.

Current Stable Version

2.41.2

Maven


<!-- pom.xml -->
<dependency>
    <groupId>com.relevantcodes</groupId>
    <artifactId>extentreports</artifactId>
    <version>2.41.2</version>
</dependency>
                        

What's new in 2.41.2

Version 2.41.2 launched on 10/20/2016. Here is what's new:

  • fix (#611) Fixes issue with replaceExisting causing exceptions while parsing
  • fix (#612) The stackTrace messages containing HTML were breaking the report
  • improvement (#495) ExtentTest is now serializable

Basic Usage

This section demonstrates basic methods that you will be using with this library.

Initializing Report

To initialize the report, you must create an instance of ExtentReports. It is possible to initialize in the following different ways:


ExtentReports(String filePath, Boolean replaceExisting, DisplayOrder displayOrder, NetworkMode networkMode, Locale locale)
ExtentReports(String filePath, Boolean replaceExisting, DisplayOrder displayOrder, NetworkMode networkMode)
ExtentReports(String filePath, Boolean replaceExisting, DisplayOrder displayOrder, Locale locale)
ExtentReports(String filePath, Boolean replaceExisting, DisplayOrder displayOrder)
ExtentReports(String filePath, Boolean replaceExisting, NetworkMode networkMode, Locale locale)
ExtentReports(String filePath, Boolean replaceExisting, NetworkMode networkMode)
ExtentReports(String filePath, NetworkMode networkMode)
ExtentReports(String filePath, Boolean replaceExisting, Locale locale)
ExtentReports(String filePath, Boolean replaceExisting)
ExtentReports(String filePath, Locale locale)
ExtentReports(String filePath)
                    
  • filePath - path of the file, in .htm or .html format
  • replaceExisting - Setting to overwrite (TRUE) the existing file or append to it
    • True (default): the file will be replaced with brand new markup, and all existing data will be lost. Use this option to create a brand new report
    • False: existing data will remain, new tests will be appended to the existing report. If the the supplied path does not exist, a new file will be created.
  • displayOrder
    • OLDEST_FIRST (default) - oldest test at the top, newest at the end
    • NEWEST_FIRST - newest test at the top, oldest at the end
  • networkMode
    • ONLINE (default): creates a single report file with all artifacts
    • OFFLINE - all report artifacts will be stored locally in %reportFolder%/extentreports and report will be accessible without internet connectivity
  • locale - locale of the HTML report, see list of supported locales here. To add a localized version of report, create a new .properties file as shown here.

Offline Report

To create an offline report, useNetworkMode.OFFLINE in your initialization:


ExtentReports(String filePath, Boolean replaceExisting, DisplayOrder displayOrder, NetworkMode networkMode, Locale locale)
ExtentReports(String filePath, Boolean replaceExisting, DisplayOrder displayOrder, NetworkMode networkMode)
ExtentReports(String filePath, Boolean replaceExisting, NetworkMode networkMode, Locale locale)
ExtentReports(String filePath, Boolean replaceExisting, NetworkMode networkMode)
ExtentReports(String filePath, NetworkMode networkMode)

// usage:
ExtentReports extent = ExtentReports("file-path", NetworkMode.OFFLINE);

Starting Custom Reporters

To start a custom reporter, use the following method:


extent.startReporter(ReporterType);

Presently, only 1 additional custom reporter is provided, of type DB to write all execution information to a sqlite database.

Writing to Report

Once your session is complete and you are ready to write all logs to the report, simply call the flush() method.


// writing everything to document
extent.flush();

Appending to an Existing Report

To append to a report you had previously created, simply mark replaceExisting = false and new tests will be appended to the same report.


ExtentReports extent = new ExtentReports(file-path, false);

Starting and Ending Tests

To start tests, a new instance of ExtentTest must be created. To end, simply call endTest(testInstance).


// new instance
ExtentReports extent = new ExtentReports(file-path, replaceExisting);

// starting test
ExtentTest test = extent.startTest("Test Name", "Sample description");

// step log
test.log(LogStatus.PASS, "Step details");

// ending test
extent.endTest(test);

// writing everything to document
extent.flush();

Creating Step Logs

There are 2 ways logs can be created: one that creates 3 columns and other that creates 4. Always use only 1 type of log for the test otherwise the table will become malformed.


// creates 3 columns in table: TimeStamp, Status, Details
log(LogStatus, details);
log(LogStatus logStatus, Throwable t);

// creates 4 columns in table: TimeStamp, Status, StepName, Details
log(LogStatus, stepName, details);
log(LogStatus logStatus, String stepName, Throwable t);

Getting Current RunStatus of Test

You can know the current status of the test during execution by calling the getRunStatus() method.


LogStatus status = test.getRunStatus();

Assign Test Categories

You can assign categories to tests using assignCategory(String... params) method:


test.assignCategory("Regression");
test.assignCategory("Regression", "ExtentAPI");
test.assignCategory("Regression", "ExtentAPI", "category-3", "cagegory-4", ..);

Or simply assign them when you start your test:


ExtentTest test = extent
    .startTest("Categories")
    .assignCategory("Regression", "ExtentAPI");

Assign Test Author(s)

Assign the author of the test (similar to assignCategory above):


test.assignAuthor("Anshoo");
test.assignAuthor("Anshoo", "Viren");

Adding Child Tests

To add a test node as a child of another test, use the appendChild method.


ExtentTest parent = extent.startTest("Parent");

ExtentTest child1 = extent.startTest("Child 1");
child1.log(LogStatus.INFO, "Info");

ExtentTest child2 = extent.startTest("Child 2");
child2.log(LogStatus.PASS, "Pass");

parent
    .appendChild(child1)
    .appendChild(child2);
    
extent.endTest(parent);

Inserting custom HTML

Simply insert any custom HTML in the logs by using an HTML tag:


extent.log(LogStatus.INFO, "HTML", "Usage: BOLD TEXT");

Insert Snapshots

To add a screen-shot, simply call addScreenCapture. This method returns the HTML with tag which can be used anywhere in the log details.


test.log(LogStatus.INFO, "Snapshot below: " + test.addScreenCapture("screenshot-path"));

Relative paths starting with . and / are supported. If you using an absolute path, file:/// will be automatically be appended for the image to load.

To insert a Base64 screenshot, simply use the string with addBase64ScreenShot method:


test.log(LogStatus.INFO, "Snapshot below: " + test.test.addBase64ScreenShot("base64String"));

Insert Screencast

To add a screencast/recording of your test run, use the addScreencast method anywhere in the log details:


test.log(LogStatus.INFO, "Screencast below: " + test.addScreencast("screencast-path"));

Relative paths starting with . and / are supported. If you using an absolute path, file:/// will be automatically be appended for the screencst to load.

Adding System Info

It is possible to add system or environment info for your using using the addSystemInfo method. It is an overloaded method that allows you to add via a Map or string pairs.

By default, the OS, User Name, Java Version and Host Name will be available. You do not have to add these 4 items manually.


extent.addSystemInfo("Selenium Version", "2.46");
extent.addSystemInfo("Environment", "Prod");

Map sysInfo = new HashMap();
sysInfo.put("Selenium Version", "2.46");
sysInfo.put("Environment", "Prod");

extent.addSystemInfo(sysInfo);

Clear all resources

Call close() at the very end of your session to clear all resources. If any of your test ended abruptly causing any side-affects (not all logs sent to ExtentReports, information missing), this method will ensure that the test is still appended to the report with a warning message.


extent.close();

You should call close() only once, at the very end (in @AfterSuite for example) as it closes the underlying stream. Once this method is called, calling any Extent method will throw an error.

Internally, this method does everything that flush() does by writing to the text file. Here are a few differences between close and flush::

  • If you fail to end any of your tests safely (by calling endTest), close will still show them in the report with a warning sign. Calling Flush will only write tests to the report once they are ended
  • Close does not write SystemInfo to the report (flush does)
  • You can call flush any number of times for a report session but close only once, because:
  • Close closes the underlying stream and destroys the source references so no other Extent commands can be used

Connect to ExtentX

If you are using ExtentX as the report server, it will be required to provide the host & port of MongoDB to connect:


// if MongoDB on localhost, port: 27017
extent.x();

// if MongoDB on custom host & port
extent.x(host, port);

Extent uses the same way to connect to MongoDB as used by MongoClient class. To see additional connection options, see MongoClient javadoc. The MongoClient connect options are used in x() the same way:


// from MongoClient docs:
MongoClient mongoClient1 = new MongoClient();
MongoClient mongoClient1 = new MongoClient("localhost");
MongoClient mongoClient2 = new MongoClient("localhost", 27017);
MongoClient mongoClient4 = new MongoClient(new ServerAddress("localhost"));

// uses same options as MongoClient
extent.x();
extent.x("localhost");
extent.x("localhost", 27017);
extent.x(new ServerAddress("localhost"));

Examples

This section shows basic examples to get started with ExtentReports 2.0. For multi-threaded execution models, see section Creating Parallel Runs.

Basic Usage Example

A simple example with a static method.


import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;

public class Main {
    private static ExtentReports extent;

    public static void main(String[] args) {
        extent = new ExtentReports("file-path", true);

        // creates a toggle for the given test, adds all log events under it    
        ExtentTest test = extent.startTest("My First Test", "Sample description");

        // log(LogStatus, details)
        test.log(LogStatus.INFO, "This step shows usage of log(logStatus, details)");

        // report with snapshot
        String img = test.addScreenCapture("img-path");
        test.log(LogStatus.INFO, "Image", "Image example: " + img);
        
        // end test
        extent.endTest(test);
        
        // calling flush writes everything to the log file
        extent.flush();
    }
}

JUnit Example

See this article by Richard Kieft showing JUnit usage with @Rule annotation.

TestNG Example

A simple example that uses a global ExtentReports instance showing 2 tests that check for Google search button text. The test with the assert will fail.


public class SingleLogTest extends BaseExample {
    @Test
    public void passTest() {
        test = extent.startTest("passTest");
        test.log(LogStatus.PASS, "Pass");
        
        Assert.assertEquals(test.getRunStatus(), LogStatus.PASS);
    }
    
    @Test
    public void intentionalFailure() throws Exception {
        test = extent.startTest("intentionalFailure");
        throw new Exception("intentional failure");        
    }
}

public class ExtentManager {
    private static ExtentReports extent;
    
    public synchronized static ExtentReports getReporter(String filePath) {
        if (extent == null) {
            extent = new ExtentReports(filePath, true);
            
            extent
                .addSystemInfo("Host Name", "Anshoo")
                .addSystemInfo("Environment", "QA");
        }
        
        return extent;
    }
}

public abstract class BaseExample {
    protected ExtentReports extent;
    protected ExtentTest test;
    
    final String filePath = "Extent.html";

    @AfterMethod
    protected void afterMethod(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE) {
            test.log(LogStatus.FAIL, result.getThrowable());
        } else if (result.getStatus() == ITestResult.SKIP) {
            test.log(LogStatus.SKIP, "Test skipped " + result.getThrowable());
        } else {
            test.log(LogStatus.PASS, "Test passed");
        }
        
        extent.endTest(test);        
        extent.flush();
    }
    
    @BeforeSuite
    public void beforeSuite() {
        extent = ExtentManager.getReporter(filePath);
    }
    
    @AfterSuite
    protected void afterSuite() {
        extent.close();
    }
}

Creating TestNG Listener using ExtentReports

If you use ExtentReports and do not want to use it like a normal logger, it is also possible to use it as a listener for TestNG to still be able to generate a beautiful report with dashboards. See this post for more information.

Creating Parallel Runs

This section is going to be helpful for users that use a multi-threaded execution model.

Virender has created a tutorial to show a few ways of using Extent in a parallel execution model. View it here

Depending on how your tests are structured, usage may differ for parallel classes and methods. If you require a single report for the run session, only use one instance of ExtentReports, as shown via ExtentManager class below.

Parallel Classes

Below is a sample testng.xml file for running parallel classes.


<?xml version="1.0" encoding="UTF-8"?>
<suite name="Extent Parallel Test" parallel="classes" thread-count="2">
    <test name="ExtentWithParallelClasses">
        <classes>
            <class name="ParallelClass1" />
            <class name="ParallelClass2" />
        </classes>
    </test>
</suite>

Note that the test instance for each class is local. That's because each class runs in its own thread, so each thread must have its own instance of test object.


public class ParallelClass1 extends BaseClass {
    @Test
    public void parallelClass1TestResultMustEqualPass() {
        ExtentTestManager.getTest().log(LogStatus.PASS, "Log from threadId: " + Thread.currentThread().getId());
        ExtentTestManager.getTest().log(LogStatus.INFO, "Log from threadId: " + Thread.currentThread().getId());
        
        Assert.assertEquals(ExtentTestManager.getTest().getRunStatus(), LogStatus.PASS);
    }
}

public class ParallelClass2 extends BaseClass {
    @Test
    public void parallelClass2TestResultMustEqualFail() throws Exception {
        ExtentTestManager.getTest().log(LogStatus.FAIL, "Log from threadId: " + Thread.currentThread().getId());
        ExtentTestManager.getTest().log(LogStatus.INFO, "Log from threadId: " + Thread.currentThread().getId());
        
        throw new Exception("intentional failure");
    }
}

public class ExtentTestManager {  
    static Map extentTestMap = new HashMap();
    static ExtentReports extent = ExtentManager.getReporter();

    public static synchronized ExtentTest getTest() {
        return extentTestMap.get((int) (long) (Thread.currentThread().getId()));
    }

    public static synchronized void endTest() {
        extent.endTest(extentTestMap.get((int) (long) (Thread.currentThread().getId())));
    }

    public static synchronized ExtentTest startTest(String testName) {
        return startTest(testName, "");
    }

    public static synchronized ExtentTest startTest(String testName, String desc) {
        ExtentTest test = extent.startTest(testName, desc);
        extentTestMap.put((int) (long) (Thread.currentThread().getId()), test);

        return test;
    }
}

public class ExtentManager {
    static ExtentReports extent;
    final static String filePath = "Extent.html";
    
    public synchronized static ExtentReports getReporter() {
        if (extent == null) {
            extent = new ExtentReports(filePath, true);
        }
        
        return extent;
    }
}

public abstract class BaseClass {
    @BeforeMethod
    public void beforeMethod(Method method) {
        ExtentTestManager.startTest(method.getName());
    }
    
    @AfterMethod
    protected void afterMethod(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE) {
            ExtentTestManager.getTest().log(LogStatus.FAIL, result.getThrowable());
        } else if (result.getStatus() == ITestResult.SKIP) {
            ExtentTestManager.getTest().log(LogStatus.SKIP, "Test skipped " + result.getThrowable());
        } else {
            ExtentTestManager.getTest().log(LogStatus.PASS, "Test passed");
        }
        
        ExtentManager.getReporter().endTest(ExtentTestManager.getTest());        
        ExtentManager.getReporter().flush();
    }
    
    protected String getStackTrace(Throwable t) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        return sw.toString();
    }
}

Parallel Methods

Below is a sample testng.xml file for running parallel methods.


<?xml version="1.0" encoding="UTF-8"?>
<suite name="Extent Parallel Test" parallel="methods">
    <test name="ExtentWithParallelMethods" allow-return-values="true">
        <classes>
            <class name="ParallelMethodsTest" />
        </classes>
    </test>
</suite>

An important thing to note when executing parallel methods is that, since each @Test method runs in its own thread, you must start and end your tests in the same @Test itself.


@Test
public class ParallelMethodsTest {
    private final String filePath = "ParallelMethodsTest.html";
    
    @BeforeSuite
    public void beforeSuite() {
        extent = ExtentManager.getReporter(filePath);
    }
    
    @Test
    public void parallelTest01() {
        ExtentTest test = ExtentTestManager.startTest(Thread.currentThread().getStackTrace()[1].getMethodName());
        test.log(LogStatus.INFO, "Log from threadId: " + Thread.currentThread().getId());
        test.log(LogStatus.INFO, "Log from threadId: " + Thread.currentThread().getId());
        ExtentTestManager.endTest();
    }
    
    @Test
    public void parallelTest02() {
        ExtentTest test = ExtentTestManager.startTest(Thread.currentThread().getStackTrace()[1].getMethodName());
        test.log(LogStatus.ERROR, "Log from threadId: " + Thread.currentThread().getId());
        test.log(LogStatus.ERROR, "Log from threadId: " + Thread.currentThread().getId());
        ExtentTestManager.endTest();
    }
	
    @AfterMethod
    public void afterEachTest(ITestResult result) {
        if (!result.isSuccess()) {
            test.log(LogStatus.FAIL, result.getThrowable());
        }
        
        extent.endTest(test);
        extent.flush();
    }
}

public class ExtentTestManager {  // new
    static Map extentTestMap = new HashMap();
    private static ExtentReports extent = ExtentManager.getReporter();

    public static synchronized ExtentTest getTest() {
        return extentTestMap.get((int) (long) (Thread.currentThread().getId()));
    }

    public static synchronized void endTest() {
        extent.endTest(extentTestMap.get((int) (long) (Thread.currentThread().getId())));
    }

    public static synchronized ExtentTest startTest(String testName) {
        return startTest(testName, "");
    }

    public static synchronized ExtentTest startTest(String testName, String desc) {
        ExtentTest test = extent.startTest(testName, desc);
        extentTestMap.put((int) (long) (Thread.currentThread().getId()), test);

        return test;
    }
}

public class ExtentManager {
    private static ExtentReports extent;
    
    public synchronized static ExtentReports getReporter(String filePath) {
        if (extent == null) {
            extent = new ExtentReports(filePath, true, NetworkMode.ONLINE);
            
            extent
                .addSystemInfo("Host Name", "Anshoo")
                .addSystemInfo("Environment", "QA");
        }
        
        return extent;
    }
    
    public synchronized static ExtentReports getReporter() {
        return extent;
    }
}

Localized Versions

Extent currently supports the following localized versions of the document:

  • English ("en") [default]
    
    new Locale("en-US");
    
  • Spanish ("es")
    
    new Locale("es-ES");
    

Configuration

New configuration items are added in most new versions, so be sure to use the latest config.xml for your report.


<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
  <configuration>
    <!-- report theme -->
    <!-- standard, dark -->
    <theme>standard</theme>
  
    <!-- document encoding -->
    <!-- defaults to UTF-8 -->
    <encoding>UTF-8</encoding>
    
    <!-- protocol for script and stylesheets -->
    <!-- defaults to https -->
    <protocol>https</protocol>
    
    <!-- title of the document -->
    <documentTitle>ExtentReports 2.0</documentTitle>
    
    <!-- report name - displayed at top-nav -->
    <reportName>Automation Report</reportName>
    
    <!-- report headline - displayed at top-nav, after reportHeadline -->
    <reportHeadline></reportHeadline>
    
    <!-- global date format override -->
    <!-- defaults to yyyy-MM-dd -->
    <dateFormat>yyyy-MM-dd</dateFormat>
    
    <!-- global time format override -->
    <!-- defaults to HH:mm:ss -->
    <timeFormat>HH:mm:ss</timeFormat>
    
    <!-- custom javascript -->
    <scripts>
      <![CDATA[
        $(document).ready(function() {
          
        });
      ]]>
    </scripts>
    
    <!-- custom styles -->
    <styles>
      <![CDATA[
        
      ]]>
    </styles>
  </configuration>
</extentreports>

The configuration file can be saved as one of your project resources of kept externally.

To use this configuration, in your Java code, call loadConfig:


loadConfig(File configFile);
loadConfig(Class clazz, String fileName);
loadConfig(Class clazz, String basePackagePath, String fileName);

Examples:


// file location: "C:\HelloWorld\extent-config.xml"
loadConfig(new File("C:\HelloWorld\extent-config.xml"));


// loadConfig(Class clazz, String fileName);
// clazz: com.relevantcodes.extentreports.ExtentReports
// packagePath of clazz: com/relevantcodes/extentreports
// final path: com/relevantcodes/extentreports/extent-config.xml
loadConfig(ExtentReports.class, "extent-config.xml");


// loadConfig(Class clazz, String basePackagePath, String fileName;
// clazz: com.relevantcodes.extentreports.ExtentReports
// 	packagePath of clazz: com/relevantcodes/extentreports
// basePackagePath: "resources"
// final path: com/relevantcodes/extentreports/resources/extent-config.xml
loadConfig(ExtentReports.class, "resources", "extent-config.xml");


<extentreports>
  <configuration>
    <!-- document encoding -->
    <!-- defaults to UTF-8 -->
    <encoding>UTF-8</encoding>

    <!-- protocol for script and stylesheets -->
    <!-- defaults to https -->
    <protocol>https</protocol>
    
    <!-- title of the document -->
    <documentTitle>ExtentReports 2.0</documentTitle>
        
    <!-- report name - displayed at top-nav -->
    <reportName>Automation Report</reportName>
        
    <!-- report headline - displayed at top-nav, after reportHeadline -->
    <reportHeadline></reportHeadline>

    <!-- global date format override -->
    <!-- defaults to yyyy-MM-dd -->
    <dateFormat>yyyy-MM-dd</dateFormat>

    <!-- global time format override -->
    <!-- defaults to HH:mm:ss -->
    <timeFormat>HH:mm:ss</timeFormat>
      
    <!-- custom javascript -->
    <scripts>
      <![CDATA[
      $(document).ready(function() {

      });
      ]]>
    </scripts>
        
    <!-- custom styles -->
    <styles>
      <![CDATA[
                
      ]]>
    </styles>
  </configuration>
</extentreports>

The configuration file can be saved as one of your project resources of kept externally.

To use this configuration, in your Java code, call loadConfig:


loadConfig(File configFile);
loadConfig(Class clazz, String fileName);
loadConfig(Class clazz, String basePackagePath, String fileName);

Examples:


// file location: "C:\HelloWorld\extent-config.xml"
loadConfig(new File("C:\HelloWorld\extent-config.xml"));


// loadConfig(Class clazz, String fileName);
// clazz: com.relevantcodes.extentreports.ExtentReports
// packagePath of clazz: com/relevantcodes/extentreports
// final path: com/relevantcodes/extentreports/extent-config.xml
loadConfig(ExtentReports.class, "extent-config.xml");


// loadConfig(Class clazz, String basePackagePath, String fileName;
// clazz: com.relevantcodes.extentreports.ExtentReports
// 	packagePath of clazz: com/relevantcodes/extentreports
// basePackagePath: "resources"
// final path: com/relevantcodes/extentreports/resources/extent-config.xml
loadConfig(ExtentReports.class, "resources", "extent-config.xml");

All customization is done at the report level and by using the config() method of ExtentReports. Remember, you can chain these methods as config() is fluent as shown below:


extent.config()
        .documentTitle("Title of the Report")
        .reportName("ExtentReports")
        .reportHeadline("A short headline.")
        .insertCustomStyles(".test { border:2px solid #444; }");

Using custom CSS

You can use your custom CSS directly in the document or load a custom css-stylesheet.


// custom styles
String style = ".test { border: 2px solid #444; }";
extent.config().insertCustomStyles(style);

// custom stylesheet
extent.config().addCustomStylesheet("C:\\css.css");

Using custom JS

Just like having the ability to change document styles, you can also add your own custom scripts.


extent.config().insertJs("$('.test').click(function(){ alert('test clicked'); });");

Setting Document Title

It is possible to change the title of the report file using:


extent.config().documentTitle("ExtentReports - DocumentTitle");

Setting Report Name

To set the report name, simply call the reportName() method:


extent.config().reportName("ExtentReports - ReportName");

The max length allowed for report-name is 20 characters.

Change Report Headline

You can remove or add your own summary by using the following config:


extent.config().reportHeadline("ExtentReports - ReportHeadline");

The max length allowed for report-headline is 70 characters.

License

Extent - Reporting API and Server Copyright (C) 2016 Anshoo Arora, RelevantCodes.com All rights reserved

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESSOR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.