Repository: authorjapps/zerocode
Pull request #336 (2019-11-07 16:07:33)

ISS-192 - files support
How to review this PR?
- Check all unit tests are green
- All code has been covered by unit tests
- At least one e2e test is present
- Changes are only in the core module


Patch:
diff --git a/core/src/main/java/org/jsmart/zerocode/core/di/main/ApplicationMainModule.java b/core/src/main/java/org/jsmart/zerocode/core/di/main/ApplicationMainModule.java
index b346b662..9bf5685d 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/di/main/ApplicationMainModule.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/di/main/ApplicationMainModule.java
@@ -26,12 +26,9 @@ import org.jsmart.zerocode.core.report.ZeroCodeReportGeneratorImpl;
 import org.jsmart.zerocode.core.runner.ZeroCodeMultiStepsScenarioRunner;
 import org.jsmart.zerocode.core.runner.ZeroCodeMultiStepsScenarioRunnerImpl;
 
-import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_CONTEXT;
-import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_HOST;
-import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_PORT;
-import static org.jsmart.zerocode.core.di.PropertyKeys.WEB_APPLICATION_ENDPOINT_CONTEXT;
-import static org.jsmart.zerocode.core.di.PropertyKeys.WEB_APPLICATION_ENDPOINT_HOST;
-import static org.jsmart.zerocode.core.di.PropertyKeys.WEB_APPLICATION_ENDPOINT_PORT;
+import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.checkAndLoadOldProperties;
+import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
+import static org.jsmart.zerocode.core.utils.SmartUtils.isValidAbsolutePath;
 
 public class ApplicationMainModule extends AbstractModule {
     private static final Logger LOGGER = Logger.getLogger(ApplicationMainModule.class.getName());
@@ -74,6 +71,11 @@ public class ApplicationMainModule extends AbstractModule {
 
     public Properties getProperties(String host) {
         final Properties properties = new Properties();
+
+        if(isValidAbsolutePath(host)){
+            return loadAbsoluteProperties(host, properties);
+        }
+
         try {
             properties.load(getClass().getClassLoader().getResourceAsStream(host));
 
@@ -91,23 +93,4 @@ public class ApplicationMainModule extends AbstractModule {
         return properties;
     }
 
-    private void checkAndLoadOldProperties(Properties properties) {
-
-        if (properties.get(WEB_APPLICATION_ENDPOINT_HOST) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_HOST) != null) {
-            Object oldPropertyValue = properties.get(RESTFUL_APPLICATION_ENDPOINT_HOST);
-            properties.setProperty(WEB_APPLICATION_ENDPOINT_HOST, oldPropertyValue != null ? oldPropertyValue.toString() : null);
-        }
-
-        if (properties.get(WEB_APPLICATION_ENDPOINT_PORT) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_PORT) != null) {
-            Object oldPropertyValue = properties.get(RESTFUL_APPLICATION_ENDPOINT_PORT);
-            properties.setProperty(WEB_APPLICATION_ENDPOINT_PORT, oldPropertyValue != null ? oldPropertyValue.toString() : null);
-        }
-
-        if (properties.get(WEB_APPLICATION_ENDPOINT_CONTEXT) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_CONTEXT) != null) {
-            Object oldPropertyValue = properties.get(RESTFUL_APPLICATION_ENDPOINT_CONTEXT);
-            properties.setProperty(WEB_APPLICATION_ENDPOINT_CONTEXT, oldPropertyValue != null ? oldPropertyValue.toString() : null);
-        }
-
-    }
-
 }
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
index 4b0ca4f5..4f32907a 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
@@ -63,6 +63,8 @@ import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeAssertionTokens.RAW
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.$VALUE;
 import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.digTypeCast;
 import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.fieldTypes;
+import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
+import static org.jsmart.zerocode.core.utils.SmartUtils.isValidAbsolutePath;
 import static org.jsmart.zerocode.core.utils.TokenUtils.getTestCaseTokens;
 import static org.jsmart.zerocode.core.utils.TokenUtils.populateParamMap;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -359,10 +361,17 @@ public class ZeroCodeAssertionsProcessorImpl implements ZeroCodeAssertionsProces
 
     private void loadAnnotatedHostProperties() {
         try {
-            properties.load(getClass().getClassLoader().getResourceAsStream(hostFileName));
-        } catch (IOException e) {
-            LOGGER.error("Problem encountered while accessing annotated host properties file '" + hostFileName + "'");
-            throw new RuntimeException(e);
+            if(isValidAbsolutePath(hostFileName)){
+               loadAbsoluteProperties(hostFileName, properties);
+            } else {
+                properties.load(getClass().getClassLoader().getResourceAsStream(hostFileName));
+            }
+
+        } catch (Exception e) {
+            String msg = "Problem encountered while accessing annotated host properties file '";
+            LOGGER.error(msg + hostFileName + "'");
+            System.err.println(msg + hostFileName + "'");
+            throw new RuntimeException(msg + e);
         }
 
         properties.keySet().stream().forEach(thisKey -> {
diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java
index 9f9d1750..d5ce4e81 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java
@@ -1,9 +1,18 @@
 package org.jsmart.zerocode.core.utils;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Properties;
 
+import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_CONTEXT;
+import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_HOST;
+import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_PORT;
+import static org.jsmart.zerocode.core.di.PropertyKeys.WEB_APPLICATION_ENDPOINT_CONTEXT;
+import static org.jsmart.zerocode.core.di.PropertyKeys.WEB_APPLICATION_ENDPOINT_HOST;
+import static org.jsmart.zerocode.core.di.PropertyKeys.WEB_APPLICATION_ENDPOINT_PORT;
+import static org.jsmart.zerocode.core.utils.SmartUtils.replaceHome;
+
 
 public class PropertiesProviderUtils {
 
@@ -27,4 +36,40 @@ public class PropertiesProviderUtils {
 
         return properties;
     }
+
+
+    public static Properties loadAbsoluteProperties(String host, Properties properties) {
+        try {
+            host = replaceHome(host);
+
+            InputStream inputStream = new FileInputStream(host);
+            properties.load(inputStream);
+
+            checkAndLoadOldProperties(properties);
+
+            return properties;
+
+        } catch (Exception exx) {
+            throw new RuntimeException(exx);
+        }
+    }
+
+    public static void checkAndLoadOldProperties(Properties properties) {
+
+        if (properties.get(WEB_APPLICATION_ENDPOINT_HOST) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_HOST) != null) {
+            Object oldPropertyValue = properties.get(RESTFUL_APPLICATION_ENDPOINT_HOST);
+            properties.setProperty(WEB_APPLICATION_ENDPOINT_HOST, oldPropertyValue != null ? oldPropertyValue.toString() : null);
+        }
+
+        if (properties.get(WEB_APPLICATION_ENDPOINT_PORT) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_PORT) != null) {
+            Object oldPropertyValue = properties.get(RESTFUL_APPLICATION_ENDPOINT_PORT);
+            properties.setProperty(WEB_APPLICATION_ENDPOINT_PORT, oldPropertyValue != null ? oldPropertyValue.toString() : null);
+        }
+
+        if (properties.get(WEB_APPLICATION_ENDPOINT_CONTEXT) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_CONTEXT) != null) {
+            Object oldPropertyValue = properties.get(RESTFUL_APPLICATION_ENDPOINT_CONTEXT);
+            properties.setProperty(WEB_APPLICATION_ENDPOINT_CONTEXT, oldPropertyValue != null ? oldPropertyValue.toString() : null);
+        }
+
+    }
 }
diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
index b9a941ef..528659a2 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
@@ -11,8 +11,14 @@ import com.google.common.io.Resources;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
+import java.io.File;
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -21,6 +27,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.apache.commons.lang.text.StrSubstitutor;
 import org.jsmart.zerocode.core.di.provider.ObjectMapperProvider;
 import org.jsmart.zerocode.core.domain.ScenarioSpec;
@@ -29,8 +36,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static java.nio.charset.Charset.defaultCharset;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher.aMatchingMessage;
 import static org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher.aNotMatchingMessage;
+import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
 import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
 import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT;
 
@@ -41,7 +50,8 @@ public class SmartUtils {
     @Inject
     private ObjectMapper mapper; //<--- remember the static methods can not use this objectMapper. So make the methods non-static if you want to use this objectMapper.
 
-    @Inject @Named("YamlMapper")
+    @Inject
+    @Named("YamlMapper")
     private ObjectMapper yamlMapper;
 
     public <T> String getItRight() throws IOException {
@@ -50,49 +60,94 @@ public class SmartUtils {
     }
 
     public <T> String getJsonDocumentAsString(String fileName) throws IOException {
-        String jsonAsString = Resources.toString(getClass().getClassLoader().getResource(fileName), StandardCharsets.UTF_8);
+        String jsonAsString = Resources.toString(getClass().getClassLoader().getResource(fileName), UTF_8);
         return jsonAsString;
     }
 
-    public static String readJsonAsString(String jsonFile){
+    static String readFile(String path, Charset encoding) throws IOException {
+        byte[] encoded = Files.readAllBytes(Paths.get(path));
+        return new String(encoded, encoding == null ? UTF_8 : encoding);
+    }
+
+    public static String readJsonAsString(String scenarioFile) {
         try {
-            return Resources.toString(Resources.getResource(jsonFile), defaultCharset());
-        } catch (IOException e) {
-            throw new RuntimeException("Exception occurred while reading the JSON file - " + jsonFile);
+            scenarioFile = replaceHome(scenarioFile);
+            if (isValidAbsolutePath(scenarioFile)) {
+                return readFile(scenarioFile, UTF_8);
+            }
+            return Resources.toString(Resources.getResource(scenarioFile), defaultCharset());
+        } catch (Exception e) {
+            LOGGER.error("Exception occurred while parsing the 'Test Scenario' file:{}. " +
+                    "Check if it is present n in correct format" + scenarioFile);
+            throw new RuntimeException("Exception occurred while reading the 'Test Scenario' file - " + scenarioFile);
         }
     }
 
-    public static String readYamlAsString(String yamlFile){
+    public static String readYamlAsString(String yamlFile) {
         return readJsonAsString(yamlFile);
     }
 
     public Map<String, Object> readJsonStringAsMap(String json) throws IOException {
         Map<String, Object> map = new HashMap<>();
-        map = mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
+        map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
+        });
 
         return map;
     }
 
     public static List<String> getAllEndPointFiles(String packageName) {
+        if(isValidAbsolutePath(packageName)){
+            return retrieveScenariosByAbsPath(packageName);
+        }
         ClassPathFactory factory = new ClassPathFactory();
         ClassPath jvmClassPath = factory.createFromJVM();
         String[] allSimulationFiles = jvmClassPath.findResources(packageName, new RegExpResourceFilter(".*", ".*\\.json$"));
         if (null == allSimulationFiles || allSimulationFiles.length == 0) {
+            LOGGER.error("Test folder is empty or not correctly setup.");
             throw new RuntimeException("NothingFoundHereException: Check the (" + packageName + ") integration test repo folder(empty?). ");
         }
 
         return Arrays.asList(allSimulationFiles);
     }
 
+    public static List<String> retrieveScenariosByAbsPath(String packageName) {
+        packageName = replaceHome(packageName);
+        try {
+            return Files.walk(Paths.get(packageName))
+                    .filter(Files::isRegularFile)
+                    .map(aPath -> aPath.toString())
+                    .collect(Collectors.toList());
+        } catch (IOException exx) {
+            LOGGER.error("Exception during reading absolute suite folder - " + exx);
+            throw new RuntimeException("Exception during reading absolute suite folder - " + exx);
+        }
+    }
+
 
     public <T> T scenarioFileToJava(String scenarioFile, Class<T> clazz) throws IOException {
-        if(scenarioFile.endsWith(".yml") || scenarioFile.endsWith(".yaml")){
+        if (scenarioFile.endsWith(".yml") || scenarioFile.endsWith(".yaml")) {
             return yamlMapper.readValue(readYamlAsString(scenarioFile), clazz);
         }
 
         return mapper.readValue(readJsonAsString(scenarioFile), clazz);
     }
 
+    public static boolean isValidAbsolutePath(String path) {
+        path = replaceHome(path);
+        Path actualPath = Paths.get(path).toAbsolutePath();
+        File file = actualPath.toFile();
+        if (file.isAbsolute() && file.exists()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static String replaceHome(String path) {
+        path = path.replaceFirst("^~", System.getProperty("user.home"));
+        return path;
+    }
+
     public List<ScenarioSpec> getScenarioSpecListByPackage(String packageName) {
         List<String> allEndPointFiles = getAllEndPointFiles(packageName);
         List<ScenarioSpec> scenarioSpecList = allEndPointFiles.stream()
@@ -175,7 +230,7 @@ public class SmartUtils {
         return sub.replace(stringWithToken);
     }
 
-    public static String getEnvPropertyValue(String envPropertyKey){
+    public static String getEnvPropertyValue(String envPropertyKey) {
 
         final String propertyValue = System.getProperty(envPropertyKey);
 
diff --git a/pom.xml b/pom.xml
index 96c551c3..3ed0f0a9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -281,6 +281,5 @@
                 </executions>
             </plugin>
         </plugins>
-    </build>
-    -->
+    </build>-->
 </project>
\ No newline at end of file


Test patch:
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java b/core/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java
index 3c9f432a..34c89952 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java
@@ -35,8 +35,8 @@ public class ZeroCodeTestReportListener extends RunListener {
         /*
          * Called when all tests have finished
          */
-        LOGGER.info("### ZeroCode: Test run completed for this runner. Generating test reports and charts. " +
-                "More help and examples are available at the README in GitHub");
+        LOGGER.info("#ZeroCode: Test run completed for this runner. Generating test reports and charts. " +
+                "\n* For more examples, helps, Kafka streams and API use-cases visit http://zerocode.io");
         generateChartsAndReports();
     }
 
diff --git a/core/src/test/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRuntimeAnnoTest.java b/core/src/test/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRuntimeAnnoTest.java
new file mode 100644
index 00000000..10497296
--- /dev/null
+++ b/core/src/test/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRuntimeAnnoTest.java
@@ -0,0 +1,225 @@
+package org.jsmart.zerocode.core.runner;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import org.hamcrest.CoreMatchers;
+import org.jsmart.zerocode.core.domain.JsonTestCase;
+import org.jsmart.zerocode.core.domain.TargetEnv;
+import org.jsmart.zerocode.core.domain.TestPackageRoot;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+/**
+ * This just POC. But look for full test coverage in CLI module(TODO - when done)
+ * Test names:
+ *   - AnnotationUtilsTest.java
+ *   - CliMainTest.java
+ */
+public class ZeroCodeUnitRuntimeAnnoTest {
+
+    ZeroCodeUnitRunner zeroCodeUnitRunner;
+
+    private static final String ANNOTATION_METHOD = "annotationData";
+    private static final String ANNOTATION_FIELDS = "declaredAnnotations";
+    private static final String ANNOTATIONS = "annotations";
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @TargetEnv("github_host.properties")
+    @RunWith(ZeroCodePackageRunner.class)
+    @TestPackageRoot("foo_folder")
+    public static class TinyPackageSuite {
+    }
+
+    @TargetEnv("foo_host.properties")
+    public static class TinyScenarioTest {
+
+        @JsonTestCase("/abcd/path")
+        @Test
+        public void testScenario1() throws Exception {
+        }
+
+    }
+
+    public static void alterAnnotation(Class<?> targetClass, Class<? extends Annotation> targetAnnotation, Annotation targetValue) {
+        try {
+            Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null);
+            method.setAccessible(true);
+
+            Object annotationData = method.invoke(targetClass);
+
+            Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
+            annotations.setAccessible(true);
+
+            Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);
+            map.put(targetAnnotation, targetValue);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Ignore("need helloworld folder to be present in the src/main")
+    @Test
+    public void testRun_junitCore() {
+        TestPackageRoot testFolder = new TestPackageRoot() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return TestPackageRoot.class;
+            }
+
+            @Override
+            public String value() {
+                return "helloworld";
+            }
+        };
+
+        TargetEnv hostProperties = new TargetEnv() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return TargetEnv.class;
+            }
+
+            @Override
+            public String value() {
+                return "github_host.properties";
+            }
+        };
+
+        alterAnnotation(TinyPackageSuite.class, TestPackageRoot.class, testFolder);
+        alterAnnotation(TinyPackageSuite.class, TargetEnv.class, hostProperties);
+
+        Result result = JUnitCore.runClasses(TinyPackageSuite.class);
+        assertThat(result.getRunCount(), CoreMatchers.is(1));
+    }
+
+    @Test
+    public void testAnnoRuntime_rootPackage() {
+
+        TestPackageRoot oldAnnotation = TinyPackageSuite.class.getAnnotationsByType(TestPackageRoot.class)[0];
+        System.out.println("oldAnnotation = " + oldAnnotation.value());
+
+        TestPackageRoot testFolder = new TestPackageRoot() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return TestPackageRoot.class;
+            }
+
+            @Override
+            public String value() {
+                return "helloworld";
+            }
+        };
+
+        alterAnnotation(TinyPackageSuite.class, TestPackageRoot.class, testFolder);
+
+        TestPackageRoot newAnno = TinyPackageSuite.class.getAnnotationsByType(TestPackageRoot.class)[0];
+        assertThat(newAnno.value(), is("helloworld"));
+    }
+
+    @Test
+    public void testAnnoRuntime_targetEnv() {
+
+        TargetEnv oldAnnotation = TinyScenarioTest.class.getAnnotationsByType(TargetEnv.class)[0];
+        System.out.println("oldAnnotation = " + oldAnnotation.value());
+
+        TargetEnv hostProperties = new TargetEnv() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return TargetEnv.class;
+            }
+
+            @Override
+            public String value() {
+                return "new_host.properties";
+            }
+        };
+
+        alterAnnotation(TinyScenarioTest.class, TargetEnv.class, hostProperties);
+
+        TargetEnv newAnno = TinyScenarioTest.class.getAnnotationsByType(TargetEnv.class)[0];
+        assertThat(newAnno.value(), is("new_host.properties"));
+    }
+
+    @Test
+    public void testAnnoRuntime_scenarioAnno() throws NoSuchMethodException {
+
+
+        JsonTestCase newAnnotation = new JsonTestCase() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return JsonTestCase.class;
+            }
+
+            @Override
+            public String value() {
+                return "/path/to/scenario.json";
+            }
+        };
+
+        alterAnnotation(TinyScenarioTest.class, JsonTestCase.class, newAnnotation);
+
+        JsonTestCase newAnno = TinyScenarioTest.class.getAnnotationsByType(JsonTestCase.class)[0];
+        assertThat(newAnno.value(), is("/path/to/scenario.json"));
+
+    }
+
+    @Test
+    public void testAnnoRuntime_scenarioAnnoMethod() throws NoSuchMethodException {
+
+
+        JsonTestCase newAnnotation = new JsonTestCase() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return JsonTestCase.class;
+            }
+
+            @Override
+            public String value() {
+                return "/path/to/scenario.json";
+            }
+        };
+
+        alterAnnotationValueField(TinyScenarioTest.class, JsonTestCase.class, newAnnotation);
+
+        JsonTestCase newAnno2 = TinyScenarioTest.class.getDeclaredMethods()[0].getAnnotationsByType(JsonTestCase.class)[0];
+        assertThat(newAnno2.value(), is("/path/to/scenario.json"));
+        //method.getMethod().getAnnotation(JsonTestCase.class);
+    }
+
+    public static void alterAnnotationValueField(Class<?> targetClass, Class<? extends Annotation> targetAnnotation, Annotation targetValue) {
+        try {
+            Method method = targetClass.getDeclaredMethod("testScenario1");
+            method.setAccessible(true);
+
+            method.getDeclaredAnnotations();
+            Class<?> superclass = method.getClass().getSuperclass();
+            Field declaredField = superclass.getDeclaredField("declaredAnnotations");
+            declaredField.setAccessible(true);
+            Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) declaredField
+                    .get(method);
+            map.put(targetAnnotation, targetValue);
+
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java b/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java
index f63b76f4..b48a4c48 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java
@@ -1,12 +1,18 @@
 package org.jsmart.zerocode.core.utils;
 
 import com.google.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import org.jsmart.zerocode.core.di.main.ApplicationMainModule;
 import org.jsmart.zerocode.core.domain.ScenarioSpec;
 import org.jsmart.zerocode.core.domain.Step;
 import org.jukito.JukitoRunner;
 import org.jukito.TestModule;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -142,6 +148,62 @@ public class SmartUtilsTest {
         assertThat(javaHomeValue, notNullValue());
 
         assertThat(SmartUtils.getEnvPropertyValue("WRONG_XYZ_INVALID"), nullValue());
+    }
+
+    @Test
+    public void testSuiteFolder_absolutePath() throws Exception {
+        // Try in target folder
+        String folder1File1 = "target/temp/unit_test_files/cherry_pick_tests/folder_a/test_case_1.json";
+        String folder2File2 = "target/temp/unit_test_files/cherry_pick_tests/folder_b/test_case_2.json";
+
+        // Create the folders
+        createCascadeIfNotExisting(folder1File1);
+        createCascadeIfNotExisting(folder2File2);
+
+        Path path1 = Paths.get(folder1File1);
+        Path path2 = Paths.get(folder2File2);
+        String absolutePath1 = path1.toFile().getAbsolutePath();
+        String absolutePath2 = path2.toFile().getAbsolutePath();
+
+        // Create the files
+        File f1 = new File(absolutePath1);
+        File f2 = new File(absolutePath2);
+        f1.createNewFile();
+        f2.createNewFile();
+
+
+        String parentFolderAbsPath = path1.getParent().getParent().toFile().getAbsolutePath();
+
+        List<String> allScenarios = SmartUtils.retrieveScenariosByAbsPath(parentFolderAbsPath);
+        assertThat(allScenarios.size(), is(2));
+        assertThat(allScenarios.get(0), containsString("unit_test_files/cherry_pick_tests/folder_b/test_case_2.json"));
+        assertThat(allScenarios.get(1), containsString("unit_test_files/cherry_pick_tests/folder_a/test_case_1.json"));
 
+        // Delete the folders/files
+        // mvn clean
+    }
+
+    @Ignore("Tested in local laptop. Ignored for Ci build. Follow testSuiteFolder_absolutePath() like flow ")
+    @Test
+    public void testSuiteFolder_symAbsolutePath() throws Exception {
+        String absPath = "~/dev/ZEROCODE_REPOS/zerocode/core/src/test/resources/unit_test_files/cherry_pick_tests";
+        List<String> allScenarios = SmartUtils.retrieveScenariosByAbsPath(absPath);
+        assertThat(allScenarios.size(), is(2));
+        assertThat(allScenarios.get(0), containsString("unit_test_files/cherry_pick_tests/folder_b/test_case_2.json"));
+        assertThat(allScenarios.get(0), containsString("cherry_pick_tests/folder_b/test_case_2.json"));
+    }
+
+    // Move this to File Util class
+    private static File createCascadeIfNotExisting(String fileName) {
+        try {
+            Path path = Paths.get(fileName);
+            Files.createDirectories(path.getParent());
+
+            File file = new File(fileName);
+
+            return file;
+        } catch (IOException exx) {
+            throw new RuntimeException("Create file '" + fileName + "' Exception" + exx);
+        }
     }
 }
\ No newline at end of file
diff --git a/http-testing/src/main/java/org/jsmart/zerocode/zerocodejavaexec/httpclient/JustHelloWorldSuite.java b/http-testing/src/main/java/org/jsmart/zerocode/zerocodejavaexec/httpclient/JustHelloWorldSuite.java
new file mode 100644
index 00000000..413cf203
--- /dev/null
+++ b/http-testing/src/main/java/org/jsmart/zerocode/zerocodejavaexec/httpclient/JustHelloWorldSuite.java
@@ -0,0 +1,16 @@
+package org.jsmart.zerocode.zerocodejavaexec.httpclient;
+
+import org.jsmart.zerocode.core.domain.Scenario;
+import org.jsmart.zerocode.core.domain.TargetEnv;
+import org.jsmart.zerocode.core.domain.TestPackageRoot;
+import org.jsmart.zerocode.core.runner.ZeroCodePackageRunner;
+import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@TargetEnv("hello_github_host.properties")
+@TestPackageRoot("/helloworld")
+@RunWith(ZeroCodePackageRunner.class)
+public class JustHelloWorldSuite {
+
+}