Fix histograms for sketches where min and max are equal (#15381)

There is a problem with Quantiles sketches and KLL Quantiles sketches.
Queries using the histogram post-aggregator fail if:

- the sketch contains at least one value, and
- the values in the sketch are all equal, and
- the splitPoints argument is not passed to the post-aggregator, and
- the numBins argument is greater than 2 (or not specified, which
  leads to the default of 10 being used)

In that case, the query fails and returns this error:

    {
      "error": "Unknown exception",
      "errorClass": "org.apache.datasketches.common.SketchesArgumentException",
      "host": null,
      "errorCode": "legacyQueryException",
      "persona": "OPERATOR",
      "category": "RUNTIME_FAILURE",
      "errorMessage": "Values must be unique, monotonically increasing and not NaN.",
      "context": {
        "host": null,
        "errorClass": "org.apache.datasketches.common.SketchesArgumentException",
        "legacyErrorCode": "Unknown exception"
      }
    }

This behaviour is undesirable, since the caller doesn't necessarily
know in advance whether the sketch has values that are diverse
enough. With this change, the post-aggregators return [N, 0, 0...]
instead of crashing, where N is the number of values in the sketch,
and the length of the list is equal to numBins. That is what they
already returned for numBins = 2.

Here is an example of a query that would fail:

    {"queryType":"timeseries",
     "dataSource": {
       "type": "inline",
       "columnNames": ["foo", "bar"],
       "rows": [
          ["abc", 42.0],
          ["def", 42.0]
       ]
     },
     "intervals":["0000/3000"],
     "granularity":"all",
     "aggregations":[
       {"name":"the_sketch", "fieldName":"bar", "type":"quantilesDoublesSketch"}],
     "postAggregations":[
       {"name":"the_histogram",
        "type":"quantilesDoublesSketchToHistogram",
        "field":{"type":"fieldAccess","fieldName":"the_sketch"},
        "numBins": 3}]}

I believe this also fixes issue #10585.
This commit is contained in:
Magnus Henoch 2023-11-16 07:01:22 +00:00 committed by GitHub
parent 53797b9e49
commit 67f45fa7bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 510 additions and 9 deletions

View File

@ -67,7 +67,7 @@ public class KllDoublesSketchToHistogramPostAggregator implements PostAggregator
public Object compute(final Map<String, Object> combinedAggregators) public Object compute(final Map<String, Object> combinedAggregators)
{ {
final KllDoublesSketch sketch = (KllDoublesSketch) field.compute(combinedAggregators); final KllDoublesSketch sketch = (KllDoublesSketch) field.compute(combinedAggregators);
final int numBins = splitPoints != null ? splitPoints.length + 1 : final int numBins = this.splitPoints != null ? this.splitPoints.length + 1 :
(this.numBins != null ? this.numBins.intValue() : DEFAULT_NUM_BINS); (this.numBins != null ? this.numBins.intValue() : DEFAULT_NUM_BINS);
if (numBins < 2) { if (numBins < 2) {
throw new IAE("at least 2 bins expected"); throw new IAE("at least 2 bins expected");
@ -77,8 +77,23 @@ public class KllDoublesSketchToHistogramPostAggregator implements PostAggregator
Arrays.fill(histogram, Double.NaN); Arrays.fill(histogram, Double.NaN);
return histogram; return histogram;
} }
final double[] histogram = sketch.getPMF(splitPoints != null ? splitPoints : final double[] splitPoints;
equallySpacedPoints(numBins, sketch.getMinItem(), sketch.getMaxItem())); if (this.splitPoints != null) {
splitPoints = this.splitPoints;
} else {
final double min = sketch.getMinItem();
final double max = sketch.getMaxItem();
if (min == max) {
// if min is equal to max, we can't create an array of equally spaced points.
// all values would go into the first bucket anyway, and the remaining
// buckets are left as zero.
final double[] histogram = new double[numBins];
histogram[0] = sketch.getN();
return histogram;
}
splitPoints = equallySpacedPoints(numBins, min, max);
}
final double[] histogram = sketch.getPMF(splitPoints);
for (int i = 0; i < histogram.length; i++) { for (int i = 0; i < histogram.length; i++) {
histogram[i] *= sketch.getN(); // scale fractions to counts histogram[i] *= sketch.getN(); // scale fractions to counts
} }

View File

@ -67,7 +67,7 @@ public class KllFloatsSketchToHistogramPostAggregator implements PostAggregator
public Object compute(final Map<String, Object> combinedAggregators) public Object compute(final Map<String, Object> combinedAggregators)
{ {
final KllFloatsSketch sketch = (KllFloatsSketch) field.compute(combinedAggregators); final KllFloatsSketch sketch = (KllFloatsSketch) field.compute(combinedAggregators);
final int numBins = splitPoints != null ? splitPoints.length + 1 : final int numBins = this.splitPoints != null ? this.splitPoints.length + 1 :
(this.numBins != null ? this.numBins.intValue() : DEFAULT_NUM_BINS); (this.numBins != null ? this.numBins.intValue() : DEFAULT_NUM_BINS);
if (numBins < 2) { if (numBins < 2) {
throw new IAE("at least 2 bins expected"); throw new IAE("at least 2 bins expected");
@ -77,8 +77,23 @@ public class KllFloatsSketchToHistogramPostAggregator implements PostAggregator
Arrays.fill(histogram, Double.NaN); Arrays.fill(histogram, Double.NaN);
return histogram; return histogram;
} }
final double[] histogram = sketch.getPMF(splitPoints != null ? splitPoints : final float[] splitPoints;
equallySpacedPoints(numBins, sketch.getMinItem(), sketch.getMaxItem())); if (this.splitPoints != null) {
splitPoints = this.splitPoints;
} else {
final float min = sketch.getMinItem();
final float max = sketch.getMaxItem();
if (min == max) {
// if min is equal to max, we can't create an array of equally spaced points.
// all values would go into the first bucket anyway, and the remaining
// buckets are left as zero.
final double[] histogram = new double[numBins];
histogram[0] = sketch.getN();
return histogram;
}
splitPoints = equallySpacedPoints(numBins, min, max);
}
final double[] histogram = sketch.getPMF(splitPoints);
for (int i = 0; i < histogram.length; i++) { for (int i = 0; i < histogram.length; i++) {
histogram[i] *= sketch.getN(); // scale fractions to counts histogram[i] *= sketch.getN(); // scale fractions to counts
} }

View File

@ -67,7 +67,7 @@ public class DoublesSketchToHistogramPostAggregator implements PostAggregator
public Object compute(final Map<String, Object> combinedAggregators) public Object compute(final Map<String, Object> combinedAggregators)
{ {
final DoublesSketch sketch = (DoublesSketch) field.compute(combinedAggregators); final DoublesSketch sketch = (DoublesSketch) field.compute(combinedAggregators);
final int numBins = splitPoints != null ? splitPoints.length + 1 : final int numBins = this.splitPoints != null ? this.splitPoints.length + 1 :
(this.numBins != null ? this.numBins.intValue() : DEFAULT_NUM_BINS); (this.numBins != null ? this.numBins.intValue() : DEFAULT_NUM_BINS);
if (numBins < 2) { if (numBins < 2) {
throw new IAE("at least 2 bins expected"); throw new IAE("at least 2 bins expected");
@ -77,8 +77,23 @@ public class DoublesSketchToHistogramPostAggregator implements PostAggregator
Arrays.fill(histogram, Double.NaN); Arrays.fill(histogram, Double.NaN);
return histogram; return histogram;
} }
final double[] histogram = sketch.getPMF(splitPoints != null ? splitPoints : final double[] splitPoints;
equallySpacedPoints(numBins, sketch.getMinItem(), sketch.getMaxItem())); if (this.splitPoints != null) {
splitPoints = this.splitPoints;
} else {
final double min = sketch.getMinItem();
final double max = sketch.getMaxItem();
if (min == max) {
// if min is equal to max, we can't create an array of equally spaced points.
// all values would go into the first bucket anyway, and the remaining
// buckets are left as zero.
final double[] histogram = new double[numBins];
histogram[0] = sketch.getN();
return histogram;
}
splitPoints = equallySpacedPoints(numBins, min, max);
}
final double[] histogram = sketch.getPMF(splitPoints);
for (int i = 0; i < histogram.length; i++) { for (int i = 0; i < histogram.length; i++) {
histogram[i] *= sketch.getN(); // scale fractions to counts histogram[i] *= sketch.getN(); // scale fractions to counts
} }

View File

@ -157,6 +157,36 @@ public class KllDoublesSketchToHistogramPostAggregatorTest
Assert.assertEquals(3.0, histogram[1], 0); Assert.assertEquals(3.0, histogram[1], 0);
} }
@Test
public void splitPointsEqualValues()
{
final double[] values = new double[] {6, 6, 6, 6, 6, 6};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new KllDoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllDoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
new double[] {3.5}, // all values are in the second bin
null
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(0.0, histogram[0], 0);
Assert.assertEquals(6.0, histogram[1], 0);
}
@Test @Test
public void numBins() public void numBins()
{ {
@ -187,6 +217,128 @@ public class KllDoublesSketchToHistogramPostAggregatorTest
Assert.assertEquals(3.0, histogram[1], 0); Assert.assertEquals(3.0, histogram[1], 0);
} }
@Test
public void oneValueTwoBins()
{
final double[] values = new double[] {1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new KllDoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllDoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
2 // two bins, the second is empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(1.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
}
@Test
public void oneValueThreeBins()
{
final double[] values = new double[] {1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new KllDoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllDoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
3 // three bins, the second and third are empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(3, histogram.length);
Assert.assertEquals(1.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
Assert.assertEquals(0.0, histogram[2], 0);
}
@Test
public void equalValuesTwoBins()
{
final double[] values = new double[] {1, 1, 1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new KllDoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllDoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
2 // two bins, the second is empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(3.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
}
@Test
public void equalValuesThreeBins()
{
final double[] values = new double[] {1, 1, 1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new KllDoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllDoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
3 // three bins, the second and third are empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(3, histogram.length);
Assert.assertEquals(3.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
Assert.assertEquals(0.0, histogram[2], 0);
}
@Test @Test
public void testResultArraySignature() public void testResultArraySignature()
{ {

View File

@ -157,6 +157,36 @@ public class KllFloatsSketchToHistogramPostAggregatorTest
Assert.assertEquals(3.0, histogram[1], 0); Assert.assertEquals(3.0, histogram[1], 0);
} }
@Test
public void splitPointsEqualValues()
{
final float[] values = new float[] {6, 6, 6, 6, 6, 6};
final TestFloatColumnSelector selector = new TestFloatColumnSelector(values);
final Aggregator agg = new KllFloatsSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllFloatsSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
new float[] {3.5f}, // all values are in the second bin
null
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(0.0, histogram[0], 0);
Assert.assertEquals(6.0, histogram[1], 0);
}
@Test @Test
public void numBins() public void numBins()
{ {
@ -187,6 +217,128 @@ public class KllFloatsSketchToHistogramPostAggregatorTest
Assert.assertEquals(3.0, histogram[1], 0); Assert.assertEquals(3.0, histogram[1], 0);
} }
@Test
public void oneValueTwoBins()
{
final float[] values = new float[] {1};
final TestFloatColumnSelector selector = new TestFloatColumnSelector(values);
final Aggregator agg = new KllFloatsSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllFloatsSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
2 // two bins, the second is empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(1.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
}
@Test
public void oneValueThreeBins()
{
final float[] values = new float[] {1};
final TestFloatColumnSelector selector = new TestFloatColumnSelector(values);
final Aggregator agg = new KllFloatsSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllFloatsSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
3 // three bins, the second and third are empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(3, histogram.length);
Assert.assertEquals(1.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
Assert.assertEquals(0.0, histogram[2], 0);
}
@Test
public void equalValuesTwoBins()
{
final float[] values = new float[] {1, 1, 1};
final TestFloatColumnSelector selector = new TestFloatColumnSelector(values);
final Aggregator agg = new KllFloatsSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllFloatsSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
2 // two bins, the second is empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(3.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
}
@Test
public void equalValuesThreeBins()
{
final float[] values = new float[] {1, 1, 1};
final TestFloatColumnSelector selector = new TestFloatColumnSelector(values);
final Aggregator agg = new KllFloatsSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new KllFloatsSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
3 // three bins, the second and third are empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(3, histogram.length);
Assert.assertEquals(3.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
Assert.assertEquals(0.0, histogram[2], 0);
}
@Test @Test
public void testResultArraySignature() public void testResultArraySignature()
{ {

View File

@ -157,6 +157,36 @@ public class DoublesSketchToHistogramPostAggregatorTest
Assert.assertEquals(3.0, histogram[1], 0); Assert.assertEquals(3.0, histogram[1], 0);
} }
@Test
public void splitPointsEqualValues()
{
final double[] values = new double[] {6, 6, 6, 6, 6, 6};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new DoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new DoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
new double[] {3.5}, // all values are in the second bin
null
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(0.0, histogram[0], 0);
Assert.assertEquals(6.0, histogram[1], 0);
}
@Test @Test
public void numBins() public void numBins()
{ {
@ -187,6 +217,128 @@ public class DoublesSketchToHistogramPostAggregatorTest
Assert.assertEquals(3.0, histogram[1], 0); Assert.assertEquals(3.0, histogram[1], 0);
} }
@Test
public void oneValueTwoBins()
{
final double[] values = new double[] {1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new DoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new DoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
2 // two bins, the second is empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(1.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
}
@Test
public void oneValueThreeBins()
{
final double[] values = new double[] {1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new DoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new DoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
3 // three bins, the second and third are empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(3, histogram.length);
Assert.assertEquals(1.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
Assert.assertEquals(0.0, histogram[2], 0);
}
@Test
public void equalValuesTwoBins()
{
final double[] values = new double[] {1, 1, 1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new DoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new DoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
2 // two bins, the second is empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(2, histogram.length);
Assert.assertEquals(3.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
}
@Test
public void equalValuesThreeBins()
{
final double[] values = new double[] {1, 1, 1};
final TestDoubleColumnSelectorImpl selector = new TestDoubleColumnSelectorImpl(values);
final Aggregator agg = new DoublesSketchBuildAggregator(selector, 8);
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < values.length; i++) {
agg.aggregate();
selector.increment();
}
final Map<String, Object> fields = new HashMap<>();
fields.put("sketch", agg.get());
final PostAggregator postAgg = new DoublesSketchToHistogramPostAggregator(
"histogram",
new FieldAccessPostAggregator("field", "sketch"),
null,
3 // three bins, the second and third are empty
);
final double[] histogram = (double[]) postAgg.compute(fields);
Assert.assertNotNull(histogram);
Assert.assertEquals(3, histogram.length);
Assert.assertEquals(3.0, histogram[0], 0);
Assert.assertEquals(0.0, histogram[1], 0);
Assert.assertEquals(0.0, histogram[2], 0);
}
@Test @Test
public void testResultArraySignature() public void testResultArraySignature()
{ {