content: /*
   * Copyright 2013-2025 Real Logic Limited.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * https://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package uk.co.real_logic.sbe.ir;
  
  import uk.co.real_logic.sbe.PrimitiveType;
  import uk.co.real_logic.sbe.xml.IrGenerator;
  import uk.co.real_logic.sbe.xml.MessageSchema;
  import uk.co.real_logic.sbe.xml.ParserOptions;
  import org.junit.jupiter.api.Test;
  
  import java.io.InputStream;
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  import java.util.List;
  
  import static org.hamcrest.MatcherAssert.assertThat;
  import static org.hamcrest.core.Is.is;
  import static uk.co.real_logic.sbe.Tests.getLocalResource;
  import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse;
  
  class BasicXmlIrGenerationTest
  {
      @Test
      void shouldGenerateIrForMessageHeader() throws Exception
      {
          try (InputStream in = getLocalResource("basic-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator irg = new IrGenerator();
  
              final Ir ir = irg.generate(schema);
              final List<Token> tokens = ir.headerStructure().tokens();
  
              assertThat(tokens.size(), is(6));
  
              /* assert all elements of node 0 */
              assertThat(tokens.get(0).signal(), is(Signal.BEGIN_COMPOSITE));
              assertThat(tokens.get(0).name(), is("messageHeader"));
              assertThat(tokens.get(0).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(0).encodedLength(), is(8));
              assertThat(tokens.get(0).offset(), is(0));
  
              /* assert all elements of node 1 */
              assertThat(tokens.get(1).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(1).name(), is("blockLength"));
              assertThat(tokens.get(1).encoding().primitiveType(), is(PrimitiveType.UINT16));
              assertThat(tokens.get(1).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(1).encodedLength(), is(2));
              assertThat(tokens.get(1).offset(), is(0));
              assertThat(tokens.get(1).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              /* assert all elements of node 2 */
              assertThat(tokens.get(2).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(2).name(), is("templateId"));
              assertThat(tokens.get(2).encoding().primitiveType(), is(PrimitiveType.UINT16));
              assertThat(tokens.get(2).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(2).encodedLength(), is(2));
              assertThat(tokens.get(2).offset(), is(2));
              assertThat(tokens.get(2).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              /* assert all elements of node 3 */
              assertThat(tokens.get(3).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(3).name(), is("schemaId"));
              assertThat(tokens.get(3).encoding().primitiveType(), is(PrimitiveType.UINT16));
              assertThat(tokens.get(3).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(3).encodedLength(), is(2));
              assertThat(tokens.get(3).offset(), is(4));
              assertThat(tokens.get(3).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              /* assert all elements of node 4 */
              assertThat(tokens.get(4).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(4).name(), is("version"));
              assertThat(tokens.get(4).encoding().primitiveType(), is(PrimitiveType.UINT16));
              assertThat(tokens.get(4).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(4).encodedLength(), is(2));
              assertThat(tokens.get(4).offset(), is(6));
              assertThat(tokens.get(4).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              /* assert all elements of node 6 */
              assertThat(tokens.get(5).signal(), is(Signal.END_COMPOSITE));
              assertThat(tokens.get(5).name(), is("messageHeader"));
              assertThat(tokens.get(5).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(5).encodedLength(), is(8));
              assertThat(tokens.get(5).offset(), is(0));
          }
      }
  
      @Test
      void shouldGenerateIrForBasicMessage() throws Exception
      {
          try (InputStream in = getLocalResource("basic-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator irg = new IrGenerator();
              final Ir ir = irg.generate(schema);
  
              final List<Token> tokens = ir.getMessage(50001);
  
              assertThat(tokens.size(), is(5));
  
              /* assert all elements of node 0 */
              assertThat(tokens.get(0).signal(), is(Signal.BEGIN_MESSAGE));
              assertThat(tokens.get(0).name(), is("TestMessage50001"));
              assertThat(tokens.get(0).id(), is(50001));
              assertThat(tokens.get(0).encodedLength(), is(16));
              assertThat(tokens.get(0).offset(), is(0));
  
              /* assert all elements of node 1 */
              assertThat(tokens.get(1).signal(), is(Signal.BEGIN_FIELD));
              assertThat(tokens.get(1).name(), is("Tag40001"));
              assertThat(tokens.get(1).id(), is(40001));
              assertThat(tokens.get(1).encodedLength(), is(4));
              assertThat(tokens.get(1).offset(), is(0));
  
              /* assert all elements of node 2 */
              assertThat(tokens.get(2).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(2).name(), is("uint32"));
              assertThat(tokens.get(2).encoding().primitiveType(), is(PrimitiveType.UINT32));
              assertThat(tokens.get(2).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(2).encodedLength(), is(4));
              assertThat(tokens.get(2).offset(), is(0));
              assertThat(tokens.get(2).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              /* assert all elements of node 3 */
              assertThat(tokens.get(3).signal(), is(Signal.END_FIELD));
              assertThat(tokens.get(3).name(), is("Tag40001"));
              assertThat(tokens.get(3).id(), is(40001));
              assertThat(tokens.get(3).encodedLength(), is(4));
              assertThat(tokens.get(3).offset(), is(0));
  
              /* assert all elements of node 4 */
              assertThat(tokens.get(4).signal(), is(Signal.END_MESSAGE));
              assertThat(tokens.get(4).name(), is("TestMessage50001"));
              assertThat(tokens.get(4).id(), is(50001));
              assertThat(tokens.get(4).encodedLength(), is(16));
              assertThat(tokens.get(4).offset(), is(0));
          }
      }
  
      @Test
      void shouldGenerateIrForMessageWithVariableLengthField() throws Exception
      {
          try (InputStream in = getLocalResource("basic-variable-length-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator irg = new IrGenerator();
              final Ir ir = irg.generate(schema);
  
              final List<Token> tokens = ir.getMessage(1);
  
              assertThat(tokens.size(), is(8));
  
              assertThat(tokens.get(0).signal(), is(Signal.BEGIN_MESSAGE));
              assertThat(tokens.get(0).name(), is("TestMessage1"));
              assertThat(tokens.get(0).id(), is(1));
              assertThat(tokens.get(0).encodedLength(), is(0));
              assertThat(tokens.get(0).offset(), is(0));
  
              assertThat(tokens.get(1).signal(), is(Signal.BEGIN_VAR_DATA));
              assertThat(tokens.get(1).name(), is("encryptedNewPassword"));
              assertThat(tokens.get(1).id(), is(1404));
              assertThat(tokens.get(1).encodedLength(), is(0));
              assertThat(tokens.get(1).offset(), is(0));
  
              assertThat(tokens.get(2).signal(), is(Signal.BEGIN_COMPOSITE));
              assertThat(tokens.get(2).name(), is("varDataEncoding"));
              assertThat(tokens.get(2).encodedLength(), is(-1));
              assertThat(tokens.get(2).offset(), is(0));
  
              assertThat(tokens.get(3).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(3).name(), is("length"));
              assertThat(tokens.get(3).encoding().primitiveType(), is(PrimitiveType.UINT8));
              assertThat(tokens.get(3).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(3).encodedLength(), is(1));
              assertThat(tokens.get(3).offset(), is(0));
              assertThat(tokens.get(3).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              assertThat(tokens.get(4).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(4).name(), is("varData"));
              assertThat(tokens.get(4).encoding().primitiveType(), is(PrimitiveType.CHAR));
              assertThat(tokens.get(4).id(), is(Token.INVALID_ID));
              assertThat(tokens.get(4).encodedLength(), is(-1));
              assertThat(tokens.get(4).offset(), is(1));
              assertThat(tokens.get(4).encoding().byteOrder(), is(ByteOrder.LITTLE_ENDIAN));
  
              assertThat(tokens.get(5).signal(), is(Signal.END_COMPOSITE));
              assertThat(tokens.get(5).name(), is("varDataEncoding"));
              assertThat(tokens.get(5).encodedLength(), is(-1));
              assertThat(tokens.get(5).offset(), is(0));
  
              assertThat(tokens.get(6).signal(), is(Signal.END_VAR_DATA));
              assertThat(tokens.get(6).name(), is("encryptedNewPassword"));
              assertThat(tokens.get(6).id(), is(1404));
              assertThat(tokens.get(6).encodedLength(), is(0));
              assertThat(tokens.get(6).offset(), is(0));
  
              assertThat(tokens.get(7).signal(), is(Signal.END_MESSAGE));
              assertThat(tokens.get(7).name(), is("TestMessage1"));
              assertThat(tokens.get(7).id(), is(1));
              assertThat(tokens.get(7).encodedLength(), is(0));
              assertThat(tokens.get(7).offset(), is(0));
          }
      }
  
      @Test
      void shouldGenerateIrForMessageWithRepeatingGroupWithEmbeddedDimensions() throws Exception
      {
          try (InputStream in = getLocalResource("basic-group-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator irg = new IrGenerator();
              final Ir ir = irg.generate(schema);
  
              /* 0=msg, 1=field, 2=enc, 3=fieldend, 4=group, 5=comp, 6=enc, 7=enc, 8=compend, ... */
              final int groupIdx = 4;
              final int dimensionsCompIdx = 5;
              final int dimensionsBlEncIdx = 6;
              final int dimensionsNigEncIdx = 7;
  
              final List<Token> tokens = ir.getMessage(1);
  
              /* assert on the group token */
              assertThat(tokens.get(groupIdx).signal(), is(Signal.BEGIN_GROUP));
              assertThat(tokens.get(groupIdx).name(), is("Entries"));
              assertThat(tokens.get(groupIdx).id(), is(2));
  
              /* assert on the comp token for dimensions */
              assertThat(tokens.get(dimensionsCompIdx).signal(), is(Signal.BEGIN_COMPOSITE));
              assertThat(tokens.get(dimensionsCompIdx).name(), is("groupSizeEncoding"));
  
              /* assert on the enc token for dimensions blockLength */
              assertThat(tokens.get(dimensionsBlEncIdx).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(dimensionsBlEncIdx).name(), is("blockLength"));
  
              /* assert on the enc token for dimensions numInGroup */
              assertThat(tokens.get(dimensionsNigEncIdx).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(dimensionsNigEncIdx).name(), is("numInGroup"));
          }
      }
  
      @Test
      void shouldGenerateIrForMessageWithRepeatingGroupWithEmbeddedDimensionsDefaultDimensionType() throws Exception
      {
          try (InputStream in = getLocalResource("embedded-length-and-count-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator irg = new IrGenerator();
              final Ir ir = irg.generate(schema);
  
              /* 0=msg, 1=field, 2=enc, 3=fieldend, 4=group, 5=comp, 6=enc, 7=enc, 8=compend, 9=field, ... */
              final int groupIdx = 4;
              final int dimensionsCompIdx = 5;
              final int fieldInGroupIdx = 9;
  
              final List<Token> tokens = ir.getMessage(1);
  
              assertThat(tokens.get(groupIdx).signal(), is(Signal.BEGIN_GROUP));
              assertThat(tokens.get(groupIdx).name(), is("ListOrdGrp"));
              assertThat(tokens.get(groupIdx).id(), is(73));
  
              assertThat(tokens.get(dimensionsCompIdx).signal(), is(Signal.BEGIN_COMPOSITE));
              assertThat(tokens.get(dimensionsCompIdx).name(), is("groupSizeEncoding"));
  
              assertThat(tokens.get(fieldInGroupIdx).signal(), is(Signal.BEGIN_FIELD));
              assertThat(tokens.get(fieldInGroupIdx).name(), is("ClOrdID"));
          }
      }
  
      @Test
      void shouldGenerateIrForMessageWithVariableLengthFieldWithEmbeddedLength() throws Exception
      {
          try (InputStream in = getLocalResource("embedded-length-and-count-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator irg = new IrGenerator();
              final Ir ir = irg.generate(schema);
  
              /* 0=msg, 1=field, 2=enc, 3=fieldend, 4=field, 5=comp, 6=enc, 7=enc, 8=compend, 9=fieldend */
              final int lengthFieldIdx = 4;
              final int lengthEncIdx = 6;
              final int dataEncIdx = 7;
  
              final List<Token> tokens = ir.getMessage(2);
  
              /* assert the varDataEncoding field node is formed correctly */
              assertThat(tokens.get(lengthFieldIdx).signal(), is(Signal.BEGIN_VAR_DATA));
              assertThat(tokens.get(lengthFieldIdx).name(), is("EncryptedPassword"));
              assertThat(tokens.get(lengthFieldIdx).id(), is(1402));
  
              /* assert the length node has correct values */
              assertThat(tokens.get(lengthEncIdx).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(lengthEncIdx).encoding().primitiveType(), is(PrimitiveType.UINT8));
  
              /* assert the group node has the right IrId and xRefIrId, etc. */
              assertThat(tokens.get(dataEncIdx).signal(), is(Signal.ENCODING));
              assertThat(tokens.get(dataEncIdx).encoding().primitiveType(), is(PrimitiveType.CHAR));
          }
      }
  
      @Test
      void shouldRegenerateIrWithSameByteOrder() throws Exception
      {
          try (InputStream in = getLocalResource("example-bigendian-test-schema.xml"))
          {
              final MessageSchema schema = parse(in, ParserOptions.DEFAULT);
              final IrGenerator generator = new IrGenerator();
              final Ir firstIr = generator.generate(schema);
              final ByteBuffer firstIrOutputBuffer = ByteBuffer.allocate(16 * 1024);
  
              final int length;
              try (IrEncoder firstIrEncoder = new IrEncoder(firstIrOutputBuffer, firstIr))
              {
                  length = firstIrEncoder.encode();
              }
  
              final Ir secondIr;
              try (IrDecoder firstIrDecoder = new IrDecoder(firstIrOutputBuffer.slice(0, length)))
              {
                  secondIr = firstIrDecoder.decode();
              }
  
              assertThat(secondIr.byteOrder(), is(firstIr.byteOrder()));
          }
      }
  }
  