package com.aqua.anttask.jsystem; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import jsystem.framework.FrameworkOptions; import jsystem.framework.JSystemProperties; import jsystem.framework.scenario.Parameter.ParameterType; import jsystem.framework.scenario.ParametersManager; import jsystem.framework.scenario.flow_control.datadriven.CsvDataProvider; import jsystem.framework.scenario.flow_control.datadriven.DataCollectorException; import jsystem.framework.scenario.flow_control.datadriven.DataProvider; import jsystem.utils.StringUtils; import jsystem.utils.beans.BeanUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.taskdefs.MacroInstance; public class JSystemDataDrivenTask extends PropertyReaderTask { private static final String DELIMITER = ";"; static Logger log = Logger.getLogger(JSystemDataDrivenTask.class.getName()); private String type; private String file; private String param; private String lineIndexes; private boolean shuffle; private long shuffleSeed; private boolean reverseOrder; private List<Map<String, Object>> data; private int iterationNum = 0; public void execute() throws BuildException { if (!JSystemAntUtil.doesContainerHaveEnabledTests(getUuid())) { return; } loadParameters(); final DataProvider provider = initProvider(); try { // in case file is a reference, if not the file name will be as entered by the user - nir String fileName = (String) ParametersManager.replaceReferenceWithValue(file, ParameterType.FILE); data = provider.provide(new File(file), param); } catch (Exception e) { log.log(Level.WARNING, "Failed to collect data due to " + e.getMessage()); return; } if (data == null || data.size() == 0) { log.log(Level.INFO, "Invalid data"); return; } filterData(); convertDataToLoop(); if (shuffle) { shuffleData(); } if (reverseOrder) { Collections.reverse(data); } super.execute(); } private DataProvider initProvider() { if (StringUtils.isEmpty(type)) { log.log(Level.WARNING, "No data provider type was specified. Rolling back to CSV provider"); return new CsvDataProvider(); } final String allProviderTypes = JSystemProperties.getInstance().getPreferenceOrDefault( FrameworkOptions.DATA_PROVIDER_CLASSES); if (StringUtils.isEmpty(allProviderTypes)) { log.log(Level.WARNING, "No providers were specified in the framework options. Rolling back to CSV provider"); return new CsvDataProvider(); } List<DataProvider> dataProvidersList = new ArrayList<DataProvider>(); for (String providerType : allProviderTypes.split(DELIMITER)) { final DataProvider provider = BeanUtils.createInstanceFromClassName(providerType, DataProvider.class); if (provider != null) { dataProvidersList.add(provider); } } for (DataProvider provider : dataProvidersList) { if (provider.getName() != null && provider.getName().trim().equals(type.trim())) { return provider; } } log.log(Level.WARNING, "No provider was found with name " + type + ". Rolling back to CSV provider"); return new CsvDataProvider(); } private void shuffleData() { if (shuffleSeed <= 0) { Collections.shuffle(data); } else { Collections.shuffle(data, new Random(shuffleSeed)); } } private void loadParameters() { type = getParameterFromProperties("Type", new CsvDataProvider().getName()); file = getParameterFromProperties("File", ""); param = getParameterFromProperties("Parameter", ""); try { param = ParametersManager.replaceAllReferenceValues(param, ParameterType.STRING); } catch (Exception e) { log.log(Level.SEVERE, "Error trying to replace reference parameters for input: " + param, e); } lineIndexes = getParameterFromProperties("LineIndexes", ""); shuffle = Boolean.valueOf(getParameterFromProperties("Shuffle", "false")); shuffleSeed = Long.parseLong(getParameterFromProperties("ShuffleSeed", "0")); reverseOrder = Boolean.parseBoolean(getParameterFromProperties("ReverseOrder", "false")); } /** * Change the data received from the collector to include only the lines * that are specified in the line indexes parameter */ private void filterData() { if (null == lineIndexes || lineIndexes.isEmpty()) { return; } final List<Integer> requiredNumbers = convertStringOfNumbersToList(lineIndexes.trim()); if (null == requiredNumbers || requiredNumbers.size() == 0) { return; } final List<Map<String, Object>> filteredData = new ArrayList<Map<String, Object>>(); for (int lineNumber : requiredNumbers) { // Notice that the line indexes are one-based if (data.size() < lineNumber) { continue; } filteredData.add(data.get(lineNumber - 1)); } if (filteredData.size() > 0) { // Only if there is something in the filtered data we will replace // the data with the filtered one. We do this to avoid exception at // run time when trying to iterate over empty list data = filteredData; } } private void convertDataToLoop() { final String paramName = data.get(0).keySet().toArray(new String[] {})[0]; StringBuilder sb = new StringBuilder(); for (Map<String, Object> dataRow : data) { sb.append(DELIMITER).append(!StringUtils.isEmpty(dataRow.get(paramName).toString())?dataRow.get(paramName):" "); } // Actually, we are not using this parameter, but we need it in order // for the the task to work. setParam("unusedparam"); // And, we are also not really using the list values, only pass it to // the for task in order to create the number of iterations required. setList(sb.toString().replaceFirst(DELIMITER, "")); } private static List<Integer> convertStringOfNumbersToList(final String numbers) { final Set<Integer> result = new HashSet<Integer>(); for (String numberStr : numbers.split(",")) { try { if (numberStr.contains("-")) { final String rangeNumbersStr[] = numberStr.split("-"); for (int i = Integer.parseInt(rangeNumbersStr[0]); i <= Integer.parseInt(rangeNumbersStr[1]); i++) { if (i > 0) { result.add(i); } } } else { int tempInt = Integer.parseInt(numberStr); if (tempInt > 0) { result.add(Integer.parseInt(numberStr)); } } } catch (NumberFormatException e) { continue; } } final List<Integer> sortedResult = new ArrayList<Integer>(result); Collections.sort(sortedResult); return sortedResult; } @Override protected void doSequentialIteration(String val) { MacroInstance instance = new MacroInstance(); instance.setProject(getProject()); instance.setOwningTarget(getOwningTarget()); instance.setMacroDef(getMacroDef()); Map<String, Object> dataRow = data.get(iterationNum++); for (String key : dataRow.keySet()) { if (dataRow.get(key) == null) { continue; } getProject().setProperty(key, dataRow.get(key).toString()); } // This parameter is not really used but we need to pass it to the for // loop. instance.setDynamicAttribute(getParam().toLowerCase(), val); instance.execute(); } public String getFile() { return file; } public void setFile(String file) { this.file = file; } public String getLineIndexes() { return lineIndexes; } public void setLineIndexes(String lineIndexes) { this.lineIndexes = lineIndexes; } }