parameterFrameworkFields)
+ {
+ this.parameterFrameworkFields = parameterFrameworkFields;
+ }
+}
diff --git a/src/main/java/com/xceptance/neodymium/module/statement/testdata/DataSet.java b/src/main/java/com/xceptance/neodymium/module/statement/testdata/DataSet.java
new file mode 100644
index 000000000..5aa3752ab
--- /dev/null
+++ b/src/main/java/com/xceptance/neodymium/module/statement/testdata/DataSet.java
@@ -0,0 +1,35 @@
+package com.xceptance.neodymium.module.statement.testdata;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used to limit and override data set execution for an entire class or at the same time for a
+ * single method.
+ *
+ * The value defines the index of the data set that has to be force used for the class or method.
+ *
+ * Default is 0 which will not have any effect on execution unless there is a {@link SuppressDataSets} annotation
+ * involved. In case a class is annotated with {@link SuppressDataSets} and a test method is annotated @DataSet()
+ * or @DataSet(0) then it will override suppression and enforce the method to run with all data sets
+ *
+ * Any number above zero will enforce execution with only that data set. First data set would be equal to 1 and so on.
+ *
+ * @author m.kaufmann
+ */
+@Retention(RUNTIME)
+@Target(
+ {
+ TYPE, METHOD
+ })
+@Repeatable(DataSets.class)
+public @interface DataSet
+{
+ int value() default 0;
+
+ String id() default "";
+}
diff --git a/src/main/java/com/xceptance/neodymium/module/statement/testdata/DataSets.java b/src/main/java/com/xceptance/neodymium/module/statement/testdata/DataSets.java
new file mode 100644
index 000000000..9f12da029
--- /dev/null
+++ b/src/main/java/com/xceptance/neodymium/module/statement/testdata/DataSets.java
@@ -0,0 +1,22 @@
+package com.xceptance.neodymium.module.statement.testdata;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Do not use this annotation. This annoation is just the wrapper for repeated {@link DataSet} annotations.
+ *
+ * @author m.kaufmann
+ */
+@Retention(RUNTIME)
+@Target(
+ {
+ TYPE, METHOD
+ })
+public @interface DataSets
+{
+ DataSet[] value();
+}
diff --git a/src/main/java/com/xceptance/neodymium/module/statement/testdata/SuppressDataSets.java b/src/main/java/com/xceptance/neodymium/module/statement/testdata/SuppressDataSets.java
new file mode 100644
index 000000000..b26c18b68
--- /dev/null
+++ b/src/main/java/com/xceptance/neodymium/module/statement/testdata/SuppressDataSets.java
@@ -0,0 +1,24 @@
+package com.xceptance.neodymium.module.statement.testdata;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used to suppress automatic test case multiplication for
+ * data sets and test data.
+ *
+ * @author m.kaufmann
+ * @see DataSet
+ */
+@Retention(RUNTIME)
+@Target(
+ {
+ TYPE, METHOD
+ })
+public @interface SuppressDataSets
+{
+
+}
diff --git a/src/main/java/com/xceptance/neodymium/module/statement/testdata/TestdataStatement.java b/src/main/java/com/xceptance/neodymium/module/statement/testdata/TestdataStatement.java
new file mode 100644
index 000000000..3ab6d6410
--- /dev/null
+++ b/src/main/java/com/xceptance/neodymium/module/statement/testdata/TestdataStatement.java
@@ -0,0 +1,308 @@
+package com.xceptance.neodymium.module.statement.testdata;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.xceptance.neodymium.module.StatementBuilder;
+import com.xceptance.neodymium.module.statement.testdata.util.TestDataUtils;
+import com.xceptance.neodymium.util.Context;
+
+public class TestdataStatement extends StatementBuilder
+{
+ private static final String TEST_ID = "testId";
+
+ public static Logger LOGGER = LoggerFactory.getLogger(TestdataStatement.class);
+
+ private Statement next;
+
+ Map testData;
+
+ public TestdataStatement(Statement next, TestdataStatementData parameter)
+ {
+ this.next = next;
+
+ int currentDataSetIndex = parameter.getIndex();
+
+ testData = new HashMap<>();
+ testData.putAll(parameter.getPackageTestData());
+
+ if (currentDataSetIndex >= 0)
+ {
+ for (Entry newDataEntry : parameter.getDataSet().entrySet())
+ {
+ // only log if a data set entry overwrites an package data entry
+ if (testData.containsKey(newDataEntry.getKey()))
+ {
+ LOGGER.debug(String.format("Data entry \"%s\" overwritten by data set #%d (old: \"%s\", new: \"%s\")",
+ newDataEntry.getKey(), currentDataSetIndex + 1, testData.get(newDataEntry.getKey()),
+ newDataEntry.getValue()));
+ }
+ testData.put(newDataEntry.getKey(), newDataEntry.getValue());
+ }
+ }
+
+ }
+
+ public TestdataStatement()
+ {
+ }
+
+ @Override
+ public void evaluate() throws Throwable
+ {
+ Context.get().data.putAll(testData);
+ next.evaluate();
+ }
+
+ @Override
+ public List