static TableRow convertGenericRecordToTableRow(GenericRecord record, TableSchema schema) {
    return convertGenericRecordToTableRow(record, schema.getFields());
  }
@Test
  public void testConvertGenericRecordToTableRow() throws Exception {
    TableSchema tableSchema = new TableSchema();
    tableSchema.setFields(fields);

    // BigQuery encodes NUMERIC and BIGNUMERIC values to Avro using the BYTES type with the DECIMAL
    // logical type. AvroCoder can't apply logical types to Schemas directly, so we need to get the
    // Schema for the Bird class defined below, then replace the field used to test NUMERIC with
    // a field that has the appropriate Schema.
    Schema numericSchema = Schema.create(Type.BYTES);
    BigDecimal numeric = new BigDecimal("123456789.123456789");
    Pair<LogicalType, byte[]> numericPair = convertToByteBuffer(numeric, numericSchema);
    Schema bigNumericSchema = Schema.create(Type.BYTES);
    BigDecimal bigNumeric =
        new BigDecimal(
            "578960446186580977117854925043439539266.34992332820282019728792003956564819967");
    Pair<LogicalType, byte[]> bigNumericPair = convertToByteBuffer(bigNumeric, bigNumericSchema);

    // In order to update the Schema for NUMERIC and BIGNUMERIC values, we need to recreate all of
    // the Fields.
    List<Schema.Field> avroFields = new ArrayList<>();
    for (Schema.Field field : AvroCoder.of(Bird.class).getSchema().getFields()) {
      Schema schema = field.schema();
      if ("birthdayMoney".equals(field.name())) {
        // birthdayMoney is nullable field with type BYTES/DECIMAL.
        schema =
            Schema.createUnion(
                Schema.create(Type.NULL), numericPair.getLeft().addToSchema(numericSchema));
      } else if ("lotteryWinnings".equals(field.name())) {
        // lotteryWinnings is nullable field with type BYTES/DECIMAL.
        schema =
            Schema.createUnion(
                Schema.create(Type.NULL), bigNumericPair.getLeft().addToSchema(bigNumericSchema));
      }
      // After a Field is added to a Schema, it is assigned a position, so we can't simply reuse
      // the existing Field.
      avroFields.add(new Schema.Field(field.name(), schema, field.doc(), field.defaultVal()));
    }
    Schema avroSchema = Schema.createRecord(avroFields);

    {
      // Test nullable fields.
      GenericRecord record = new GenericData.Record(avroSchema);
      record.put("number", 5L);
      TableRow convertedRow = BigQueryAvroUtils.convertGenericRecordToTableRow(record, tableSchema);
      TableRow row = new TableRow().set("number", "5").set("associates", new ArrayList<TableRow>());
      assertEquals(row, convertedRow);
      TableRow clonedRow = convertedRow.clone();
      assertEquals(convertedRow, clonedRow);
    }
    {
      // Test type conversion for:
      // INTEGER, FLOAT, NUMERIC, TIMESTAMP, BOOLEAN, BYTES, DATE, DATETIME, TIME.
      GenericRecord record = new GenericData.Record(avroSchema);
      byte[] soundBytes = "chirp,chirp".getBytes(StandardCharsets.UTF_8);
      ByteBuffer soundByteBuffer = ByteBuffer.wrap(soundBytes);
      soundByteBuffer.rewind();
      record.put("number", 5L);
      record.put("quality", 5.0);
      record.put("birthday", 5L);
      record.put("birthdayMoney", ByteBuffer.wrap(numericPair.getRight()));
      record.put("lotteryWinnings", ByteBuffer.wrap(bigNumericPair.getRight()));
      record.put("flighted", Boolean.TRUE);
      record.put("sound", soundByteBuffer);
      record.put("anniversaryDate", new Utf8("2000-01-01"));
      record.put("anniversaryDatetime", new String("2000-01-01 00:00:00.000005"));
      record.put("anniversaryTime", new Utf8("00:00:00.000005"));
      record.put("geoPositions", new String("LINESTRING(1 2, 3 4, 5 6, 7 8)"));
      TableRow convertedRow = BigQueryAvroUtils.convertGenericRecordToTableRow(record, tableSchema);
      TableRow row =
          new TableRow()
              .set("number", "5")
              .set("birthday", "1970-01-01 00:00:00.000005 UTC")
              .set("birthdayMoney", numeric.toString())
              .set("lotteryWinnings", bigNumeric.toString())
              .set("quality", 5.0)
              .set("associates", new ArrayList<TableRow>())
              .set("flighted", Boolean.TRUE)
              .set("sound", BaseEncoding.base64().encode(soundBytes))
              .set("anniversaryDate", "2000-01-01")
              .set("anniversaryDatetime", "2000-01-01 00:00:00.000005")
              .set("anniversaryTime", "00:00:00.000005")
              .set("geoPositions", "LINESTRING(1 2, 3 4, 5 6, 7 8)");
      TableRow clonedRow = convertedRow.clone();
      assertEquals(convertedRow, clonedRow);
      assertEquals(row, convertedRow);
    }
    {
      // Test repeated fields.
      Schema subBirdSchema = AvroCoder.of(Bird.SubBird.class).getSchema();
      GenericRecord nestedRecord = new GenericData.Record(subBirdSchema);
      nestedRecord.put("species", "other");
      GenericRecord record = new GenericData.Record(avroSchema);
      record.put("number", 5L);
      record.put("associates", Lists.newArrayList(nestedRecord));
      record.put("birthdayMoney", ByteBuffer.wrap(numericPair.getRight()));
      record.put("lotteryWinnings", ByteBuffer.wrap(bigNumericPair.getRight()));
      TableRow convertedRow = BigQueryAvroUtils.convertGenericRecordToTableRow(record, tableSchema);
      TableRow row =
          new TableRow()
              .set("associates", Lists.newArrayList(new TableRow().set("species", "other")))
              .set("number", "5")
              .set("birthdayMoney", numeric.toString())
              .set("lotteryWinnings", bigNumeric.toString());
      assertEquals(row, convertedRow);
      TableRow clonedRow = convertedRow.clone();
      assertEquals(convertedRow, clonedRow);
    }
  }