/**
* Copyright 2015 Nabarun Mondal
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.noga.njexl.testing.api;
import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
/**
* Annotations for Unit Testing of API
*/
public final class Annotations {
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE )
public @interface NApiService {
/**
* If true then use this for testing - defaults true
* @return true if one needs to run, false otherwise
*/
boolean use() default true ;
/**
* The base directory where all data and script
* would be stored - default empty
* @return directory
*/
String base() default "" ;
/**
* The before class script location ( not implemented )
* @return before class script location
*/
String beforeClass() default "" ;
/**
* The after class script location ( not implemented )
* @return after class script location
*/
String afterClass() default "" ;
}
/**
* Defines the service creator,
* That is, how to create the service instance
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE )
public @interface NApiServiceCreator {
/**
* The service creator class
* Defaults to @{ServiceCreatorFactory.SimpleCreator}
* @return service creator class
*/
Class type() default ServiceCreatorFactory.SimpleCreator.class ;
/**
* String representation of the arguments
* needs to call constructor of the service creator class
* Default is empty array
* @return arguments to the constructor
*/
String[] args() default {};
}
/**
* How to specifically initialize the service object
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.CONSTRUCTOR )
public @interface NApiServiceInit {
/**
* The Spring Bean Name
* When one wants to instantiate the class using spring
* Defaults to empty string
* @return bean name
*/
String bean() default "";
/**
* String representation of the arguments
* needs to call constructor of the service class
* Defaults to empty array
* @return arguments to the constructor
*/
String[] args() default {};
}
/**
* In case of performance testing of the API is needed
*/
@Retention( RetentionPolicy.RUNTIME )
public @interface Performance{
/**
* Is this a performance test
* Defaults to true
* @return true if it is, false if it is not
*/
boolean use() default true ;
/**
* Percentile for performance
* Defaults to 90%
* @return the percentile you are seeking
*/
short percentile() default 90;
/**
* Percentile for performance
* The experimental value should not exceed this,
* Else exception will be thrown. Defaults to @{CallContainer.DEFAULT_PERCENTILE_VALUE}
* @return the expected experimental upper cut-off of the percentile value
*/
double lessThan() default CallContainer.DEFAULT_PERCENTILE_VALUE ;
}
/**
* Should we use threading or not
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
public @interface NApiThread {
/**
* Whether or not use threaded mode
* Defaults to true
* @return true/false
*/
boolean use() default true;
/**
* Number of threads
* @return no of threads, default 2
*/
int numThreads() default 2 ;
/**
*
* @return no of call per thread, default 2
*/
int numCallPerThread() default 2 ;
/**
* In Milliseconds
* @return time in ms to spawn threads
*/
int spawnTime() default 1000 ;
/**
* In Milliseconds
* @return time between two calls in same thread
*/
int pacingTime() default 1000 ;
/**
* For DCD : [D]ifferent [C]all [D]ifferent (Data)
* In case all the threads would execute different input or not
* @return true if all calls needs to execute different input, false otherwise
*/
boolean dcd() default false ;
/**
* The performance strategy if any, default off
* @return the performance strategy
*/
Performance performance() default @Performance(use=false);
}
/**
* Marks the function as API to test
*/
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.METHOD )
public @interface NApi {
/**
* If true then use this for testing
* Defaults to true
* @return true if one needs to run, false otherwise
*/
boolean use() default true ;
/**
* <pre>
* The data source location :
* [1] url
* [2] Directory with flat files
* [3] Excel files
* </pre>
* @return the location
*/
String dataSource();
/**
* The actual data table
* <pre>
* A flat file
* A table ( index ) in a url
* A data sheet in excel file
* </pre>
* @return name of the table
*/
String dataTable();
/**
* Ensures that the call order of data is random
* @return randomized if true else executes test vectors in order
*/
boolean randomize() default false;
/**
* The script that would invoke
* before any of the test vector calls started ( not implemented )
* @return relative location of the script w.r.t. base
*/
String beforeAll() default "";
/**
* The script that would invoke
* after all of the test vector calls done ( not implemented )
* @return relative location of the script w.r.t. base
*/
String afterAll() default "";
/**
* The script that would invoke
* before per test vector call
* @return relative location of the script w.r.t. base
*/
String before() default "";
/**
* The script that would invoke
* after per test vector call
* @return relative location of the script w.r.t. base
*/
String after() default "";
/**
* Global Variables which would be accessible
* per test vector call inside all the scripts
* <pre>
* The way to put vars is :
* { "x=a" , "y=b" ,...}
* This ensures x variable is assigned the value 'a'.
* </pre>
* @return global variables entries to a global dict
*/
String[] globals() default {};
}
/**
* Holder class to retain information about
* How to run the method
*/
public static class MethodRunInformation{
/**
* What is my base directory for all data/script
*/
public String base;
/**
* What is precisely my method
*/
public Method method;
/**
* The method run information
*/
public NApi nApi;
/**
* Methods threading information
*/
public NApiThread nApiThread ;
}
/**
* Gets service level information from a class
* @param c the class
* @return service level information
*/
public static NApiService NApiService(Class c){
NApiService ns = (NApiService) c.getAnnotation( NApiService.class);
if ( ns != null && ns.use() ){
return ns;
}
return null;
}
/**
* Gets service creator level information from a class
* @param c the class
* @return service creator level information
*/
public static NApiServiceCreator NApiServiceCreator(Class c){
return (NApiServiceCreator) c.getAnnotation(NApiServiceCreator.class);
}
/**
* Gets service object initialization information
* @param c the class
* @return service initialization information
*/
public static NApiServiceInit NApiServiceInit(Constructor c){
return (NApiServiceInit) c.getAnnotation(NApiServiceInit.class);
}
/**
* Gets method run details from a method
* @param m the method
* @return run details for the method
*/
public static NApi NApi(Method m){
NApi nApi = m.getAnnotation(NApi.class);
if ( nApi != null && nApi.use() ){
return nApi;
}
return null;
}
/**
* Gets method's thread run details from a method
* @param m the method
* @return Thread run details for the method
*/
public static NApiThread NApiThread(Method m){
return m.getAnnotation(NApiThread.class);
}
/**
* Gets all method run details from a class
* @param c the class
* @return all run details for all the methods in the class as a list
*/
public static List<MethodRunInformation> runs( Class c ){
NApiService ns = NApiService(c);
if ( ns == null ) { return Collections.emptyList() ; }
Method[] methods = c.getDeclaredMethods();
ArrayList<MethodRunInformation> l = new ArrayList();
for ( int i = 0 ; i < methods.length; i++ ){
NApi nApi = NApi(methods[i]);
if ( nApi == null ){ continue; }
NApiThread nApiThread = NApiThread(methods[i]);
MethodRunInformation methodRunInformation = new MethodRunInformation();
methodRunInformation.base = ns.base();
methodRunInformation.method = methods[i];
methodRunInformation.nApi = nApi ;
methodRunInformation.nApiThread = nApiThread ;
l.add(methodRunInformation);
}
return l;
}
}