/** Randomly adds fields, objects, or arrays to the provided builder. The maximum depth is 5. */ private static void addFields(Random random, XContentBuilder builder, int currentDepth) throws IOException { int numFields = RandomNumbers.randomIntBetween(random, 1, 5); for (int i = 0; i < numFields; i++) { if (currentDepth < 5 && random.nextBoolean()) { if (random.nextBoolean()) { builder.startObject(RandomStrings.randomAsciiOfLengthBetween(random, 3, 10)); addFields(random, builder, currentDepth + 1); builder.endObject(); } else { builder.startArray(RandomStrings.randomAsciiOfLengthBetween(random, 3, 10)); int numElements = RandomNumbers.randomIntBetween(random, 1, 5); boolean object = random.nextBoolean(); int dataType = -1; if (object == false) { dataType = randomDataType(random); } for (int j = 0; j < numElements; j++) { if (object) { builder.startObject(); addFields(random, builder, 5); builder.endObject(); } else { builder.value(randomFieldValue(random, dataType)); } } builder.endArray(); } } else { builder.field( RandomStrings.randomAsciiOfLengthBetween(random, 3, 10), randomFieldValue(random, randomDataType(random))); } } }
/** * Returns a tuple containing random stored field values and their corresponding expected values * once printed out via {@link * org.elasticsearch.common.xcontent.ToXContent#toXContent(XContentBuilder, ToXContent.Params)} * and parsed back via {@link org.elasticsearch.common.xcontent.XContentParser#objectText()}. * Generates values based on what can get printed out. Stored fields values are retrieved from * lucene and converted via {@link * org.elasticsearch.index.mapper.MappedFieldType#valueForDisplay(Object)} to either strings, * numbers or booleans. * * @param random Random generator * @param xContentType the content type, used to determine what the expected values are for float * numbers. */ public static Tuple<List<Object>, List<Object>> randomStoredFieldValues( Random random, XContentType xContentType) { int numValues = RandomNumbers.randomIntBetween(random, 1, 5); List<Object> originalValues = new ArrayList<>(); List<Object> expectedParsedValues = new ArrayList<>(); int dataType = RandomNumbers.randomIntBetween(random, 0, 8); for (int i = 0; i < numValues; i++) { switch (dataType) { case 0: long randomLong = random.nextLong(); originalValues.add(randomLong); expectedParsedValues.add(randomLong); break; case 1: int randomInt = random.nextInt(); originalValues.add(randomInt); expectedParsedValues.add(randomInt); break; case 2: Short randomShort = (short) random.nextInt(); originalValues.add(randomShort); expectedParsedValues.add(randomShort.intValue()); break; case 3: Byte randomByte = (byte) random.nextInt(); originalValues.add(randomByte); expectedParsedValues.add(randomByte.intValue()); break; case 4: double randomDouble = random.nextDouble(); originalValues.add(randomDouble); expectedParsedValues.add(randomDouble); break; case 5: Float randomFloat = random.nextFloat(); originalValues.add(randomFloat); if (xContentType == XContentType.CBOR) { // with CBOR we get back a float expectedParsedValues.add(randomFloat); } else if (xContentType == XContentType.SMILE) { // with SMILE we get back a double expectedParsedValues.add(randomFloat.doubleValue()); } else { // with JSON AND YAML we get back a double, but with float precision. expectedParsedValues.add(Double.parseDouble(randomFloat.toString())); } break; case 6: boolean randomBoolean = random.nextBoolean(); originalValues.add(randomBoolean); expectedParsedValues.add(randomBoolean); break; case 7: String randomString = random.nextBoolean() ? RandomStrings.randomAsciiOfLengthBetween(random, 3, 10) : randomUnicodeOfLengthBetween(random, 3, 10); originalValues.add(randomString); expectedParsedValues.add(randomString); break; case 8: byte[] randomBytes = RandomStrings.randomUnicodeOfLengthBetween(random, 10, 50) .getBytes(StandardCharsets.UTF_8); BytesArray randomBytesArray = new BytesArray(randomBytes); originalValues.add(randomBytesArray); if (xContentType == XContentType.JSON || xContentType == XContentType.YAML) { // JSON and YAML write the base64 format expectedParsedValues.add(Base64.getEncoder().encodeToString(randomBytes)); } else { // SMILE and CBOR write the original bytes as they support binary format expectedParsedValues.add(randomBytesArray); } break; default: throw new UnsupportedOperationException(); } } return Tuple.tuple(originalValues, expectedParsedValues); }
private static int randomDataType(Random random) { return RandomNumbers.randomIntBetween(random, 0, 3); }