Skip to content

Commit a3a77a2

Browse files
authored
Fix several bugs in cross-multiplication implementation (#2123)
1 parent db76b97 commit a3a77a2

File tree

4 files changed

+257
-22
lines changed

4 files changed

+257
-22
lines changed

spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ protected int estimateNumIterations(@Nullable Object dataProvider) {
6161
return UNKNOWN_ITERATIONS;
6262
}
6363

64+
if (dataProvider == null) {
65+
return UNKNOWN_ITERATIONS;
66+
}
67+
6468
// an IDataIterator probably already has the estimated size
6569
// or knows better how to calculate it
6670
if (dataProvider instanceof IDataIterator) {
@@ -107,18 +111,21 @@ protected int estimateNumIterations(Object[] dataProviders) {
107111
}
108112

109113
protected boolean haveNext(Iterator<?>[] iterators, List<DataProviderInfo> dataProviderInfos) {
114+
Assert.that(iterators.length == dataProviderInfos.size());
110115
boolean result = true;
111116

112117
for (int i = 0; i < iterators.length; i++)
113118
try {
114-
boolean hasNext = iterators[i].hasNext();
115119
if (i == 0) {
116-
result = hasNext;
117-
} else if (result != hasNext) {
118-
DataProviderInfo provider = dataProviderInfos.get(i);
119-
supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(provider.getDataProviderMethod(),
120-
createDifferentNumberOfDataValuesException(provider, hasNext), getErrorContext()));
121-
return false;
120+
result = iterators[0].hasNext();
121+
} else if (iterators[i] != null) {
122+
boolean hasNext = iterators[i].hasNext();
123+
if (result != hasNext) {
124+
DataProviderInfo provider = dataProviderInfos.get(i);
125+
supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(provider.getDataProviderMethod(),
126+
createDifferentNumberOfDataValuesException(provider, hasNext), getErrorContext()));
127+
return false;
128+
}
122129
}
123130

124131
} catch (Throwable t) {
@@ -353,6 +360,9 @@ public FeatureDataProviderIterator(IRunSupervisor supervisor, SpockExecutionCont
353360
dataVariableNames = dataVariableNames();
354361
dataProviders = createDataProviders();
355362
dataProviderIterators = createDataProviderIterators();
363+
if ((dataProviderIterators != null) && (dataProviders != null)) {
364+
Assert.that(dataProviderIterators.length == dataProviders.length);
365+
}
356366
estimatedNumIterations = estimateNumIterations(dataProviderIterators);
357367
}
358368

@@ -380,12 +390,16 @@ public Object[] next() {
380390
if (context.getErrorInfoCollector().hasErrors()) {
381391
return null;
382392
}
393+
Assert.that(dataProviders.length == context.getCurrentFeature().getDataProviders().size());
383394
firstIteration = false;
384395

385396
// advances iterators and computes args
386397
Object[] next = new Object[dataProviders.length];
387398
for (int i = 0; i < dataProviders.length; ) {
388399
try {
400+
if (dataProviderIterators[i] == null) {
401+
continue;
402+
}
389403
// if the filter block excluded an iteration
390404
// this might be called after the last iteration
391405
// so just return null if no further data is available
@@ -476,14 +490,14 @@ private IDataIterator[] createDataProviderIterators() {
476490
}
477491

478492
List<DataProviderInfo> dataProviderInfos = context.getCurrentFeature().getDataProviders();
479-
List<IDataIterator> dataIterators = new ArrayList<>(dataProviders.length);
493+
IDataIterator[] dataIterators = new IDataIterator[dataProviders.length];
480494
for (int dataProviderIndex = 0, dataVariableNameIndex = 0; dataProviderIndex < dataProviders.length; dataProviderIndex++, dataVariableNameIndex++) {
481495
String nextDataVariableName = dataVariableNames.get(dataVariableNameIndex);
482496
if ((nextDataVariableMultiplication != null)
483497
&& (nextDataVariableMultiplication.getDataVariables()[0].equals(nextDataVariableName))) {
484498

485499
// a cross multiplication starts
486-
dataIterators.add(createDataProviderMultiplier(nextDataVariableMultiplication, dataProviderIndex));
500+
dataIterators[dataProviderIndex] = createDataProviderMultiplier(nextDataVariableMultiplication, dataProviderIndex);
487501
// skip processed providers and variables
488502
int remainingVariables = nextDataVariableMultiplication.getDataVariables().length;
489503
dataVariableNameIndex += remainingVariables - 1;
@@ -497,12 +511,14 @@ private IDataIterator[] createDataProviderIterators() {
497511
nextDataVariableMultiplication = dataVariableMultiplications.hasNext() ? dataVariableMultiplications.next() : null;
498512
} else {
499513
// not a cross multiplication, just use a data provider iterator
500-
dataIterators.add(new DataProviderIterator(
514+
DataProviderInfo dataProviderInfo = dataProviderInfos.get(dataProviderIndex);
515+
dataIterators[dataProviderIndex] = new DataProviderIterator(
501516
supervisor, context, nextDataVariableName,
502-
dataProviderInfos.get(dataProviderIndex), dataProviders[dataProviderIndex]));
517+
dataProviderInfo, dataProviders[dataProviderIndex]);
518+
dataVariableNameIndex += dataProviderInfo.getDataVariables().size() - 1;
503519
}
504520
}
505-
return dataIterators.toArray(new IDataIterator[0]);
521+
return dataIterators;
506522
}
507523

508524
/**
@@ -534,6 +550,7 @@ private DataProviderMultiplier createDataProviderMultiplier(DataVariableMultipli
534550

535551
return new DataProviderMultiplier(supervisor, context,
536552
Arrays.asList(dataVariableMultiplication.getDataVariables()),
553+
multiplierProvider.multiplierProviderInfos.subList(0, 1),
537554
multiplicandProviderInfos, multiplierProvider,
538555
multiplicandProviders.toArray(new Object[0]));
539556
} else {
@@ -610,9 +627,9 @@ public DataProviderIterator(IRunSupervisor supervisor, SpockExecutionContext con
610627
DataProviderInfo providerInfo, Object provider) {
611628
super(supervisor, context);
612629
this.dataVariableNames = singletonList(Objects.requireNonNull(dataVariableName));
613-
estimatedNumIterations = estimateNumIterations(Objects.requireNonNull(provider));
630+
estimatedNumIterations = estimateNumIterations(provider);
614631
this.providerInfo = Objects.requireNonNull(providerInfo);
615-
this.provider = provider;
632+
this.provider = Objects.requireNonNull(provider);
616633
iterator = createIterator(provider, providerInfo);
617634
}
618635

@@ -670,8 +687,6 @@ private static class DataProviderMultiplier extends BaseDataIterator {
670687
/**
671688
* The provider infos for the multiplier data providers.
672689
* These are only used for constructing meaningful errors.
673-
* If {@link #multiplierProviders} is set, this will be set too,
674-
* if it is {@code null}, this will be {@code null} too.
675690
*/
676691
private final List<DataProviderInfo> multiplierProviderInfos;
677692

@@ -788,6 +803,10 @@ public DataProviderMultiplier(IRunSupervisor supervisor, SpockExecutionContext c
788803
multiplierIterators = createIterators(this.multiplierProviders, this.multiplierProviderInfos);
789804
multiplicandIterators = createIterators(this.multiplicandProviders, this.multiplicandProviderInfos);
790805

806+
if (multiplicandIterators != null) {
807+
Assert.that(multiplicandProviderInfos.size() == multiplicandIterators.length);
808+
}
809+
791810
int estimatedMultiplierIterations = estimateNumIterations(Objects.requireNonNull(multiplierProviders));
792811
int estimatedMultiplicandIterations = estimateNumIterations(Objects.requireNonNull(multiplicandProviders));
793812
estimatedNumIterations =
@@ -812,11 +831,11 @@ public DataProviderMultiplier(IRunSupervisor supervisor, SpockExecutionContext c
812831
* @param multiplicandProviders the actual providers for the sets of multiplicand values
813832
*/
814833
public DataProviderMultiplier(IRunSupervisor supervisor, SpockExecutionContext context, List<String> dataVariableNames,
815-
List<DataProviderInfo> multiplicandProviderInfos,
834+
List<DataProviderInfo> multiplierProviderInfos, List<DataProviderInfo> multiplicandProviderInfos,
816835
DataProviderMultiplier multiplierProvider, Object[] multiplicandProviders) {
817836
super(supervisor, context);
818837
this.dataVariableNames = Objects.requireNonNull(dataVariableNames);
819-
multiplierProviderInfos = null;
838+
this.multiplierProviderInfos = multiplierProviderInfos;
820839
this.multiplicandProviderInfos = multiplicandProviderInfos;
821840
this.multiplierProvider = multiplierProvider;
822841
multiplierProviders = null;
@@ -826,6 +845,10 @@ public DataProviderMultiplier(IRunSupervisor supervisor, SpockExecutionContext c
826845
multiplierIterators = new Iterator[]{multiplierProvider};
827846
multiplicandIterators = createIterators(this.multiplicandProviders, this.multiplicandProviderInfos);
828847

848+
if (multiplicandIterators != null) {
849+
Assert.that(multiplicandProviderInfos.size() == multiplicandIterators.length);
850+
}
851+
829852
int estimatedMultiplierIterations = Objects.requireNonNull(multiplierProvider).getEstimatedNumIterations();
830853
int estimatedMultiplicandIterations = estimateNumIterations(Objects.requireNonNull(multiplicandProviders));
831854
estimatedNumIterations =
@@ -943,6 +966,7 @@ private Iterator<?>[] createIterators(Object[] dataProviders, List<DataProviderI
943966
if (context.getErrorInfoCollector().hasErrors()) {
944967
return null;
945968
}
969+
Assert.that(dataProviders.length == dataProviderInfos.size());
946970

947971
Iterator<?>[] iterators = new Iterator<?>[dataProviders.length];
948972
for (int i = 0; i < dataProviders.length; i++) {
@@ -957,6 +981,7 @@ private Iterator<?>[] createIterators(Object[] dataProviders, List<DataProviderI
957981
}
958982

959983
protected Object[] extractNextValues(Iterator<?>[] iterators, List<DataProviderInfo> providerInfos) {
984+
Assert.that(iterators.length == providerInfos.size());
960985
Object[] result = new Object[iterators.length];
961986
for (int i = 0; i < iterators.length; i++) {
962987
try {

spock-specs/src/test/groovy/org/spockframework/docs/datadriven/DataSpec.groovy

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ class DataSpec extends EmbeddedSpecification {
194194
}`""" == expected
195195
}
196196

197-
def "only single data providers are combined"() {
197+
def "only single data providers are combined - part 1"() {
198198
given:
199199
def expected = '''
200200
tag::single-data-providers-combined-result1[]
@@ -239,9 +239,11 @@ class DataSpec extends EmbeddedSpecification {
239239
*.displayName
240240
.join('`\n- `')
241241
}`""" == expected
242+
}
242243

243-
when:
244-
expected = '''
244+
def "only single data providers are combined - part 2"() {
245+
given:
246+
def expected = '''
245247
tag::single-data-providers-combined-result2[]
246248
- `feature [a: 1, b: 3, c: 5, #0]`
247249
- `feature [a: 1, b: 3, c: 6, #1]`
@@ -254,7 +256,9 @@ class DataSpec extends EmbeddedSpecification {
254256
.findAll {it.startsWith('-') }
255257
.join('\n')
256258
.trim()
257-
result = runner.runSpecBody '''
259+
260+
when:
261+
def result = runner.runSpecBody '''
258262
def feature() {
259263
expect:
260264
true

0 commit comments

Comments
 (0)