code: /*
   * Copyright (c) 2021 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.sdk.client.dtd;
  
  import java.util.Arrays;
  import java.util.List;
  import java.util.Objects;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.ExecutionException;
  import java.util.stream.Collectors;
  
  import org.eclipse.milo.opcua.binaryschema.parser.BsdParser;
  import org.eclipse.milo.opcua.sdk.client.OpcUaSession;
  import org.eclipse.milo.opcua.sdk.core.NumericRange;
  import org.eclipse.milo.opcua.stack.client.UaStackClient;
  import org.eclipse.milo.opcua.stack.client.UaStackClientConfig;
  import org.eclipse.milo.opcua.stack.core.Identifiers;
  import org.eclipse.milo.opcua.stack.core.Stack;
  import org.eclipse.milo.opcua.stack.core.UaException;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
  import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
  import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
  import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReadRequest;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReadResponse;
  import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
  import org.junit.jupiter.api.Assertions;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.mockito.ArgumentMatchers;
  import org.mockito.Mockito;
  
  import static java.util.concurrent.CompletableFuture.completedFuture;
  import static org.junit.jupiter.api.Assertions.assertNotNull;
  
  class DataTypeDictionaryReaderTest {
  
      private final UaStackClient stackClient = Mockito.mock(UaStackClient.class);
      private final UaStackClientConfig config = Mockito.mock(UaStackClientConfig.class);
      private final OpcUaSession session = Mockito.mock(OpcUaSession.class);
      private final BsdParser bsdParser = Mockito.mock(BsdParser.class);
  
      private final DataTypeDictionaryReader dictionaryReader =
          new DataTypeDictionaryReader(stackClient, session, bsdParser);
  
      @BeforeEach
      void setUp() {
          Mockito.when(stackClient.getConfig()).thenReturn(config);
          Mockito.when(config.getExecutor()).thenReturn(Stack.sharedExecutor());
      }
  
      @Test
      void testReadDataTypeDictionaryBytes() throws Exception {
          int[] fragmentSizes = new int[]{1, 2, 1024, 2048, 4096, 8192};
          int[] dictionarySizes = new int[]{1, 2, 1024, 1025, 2048, 2049, 4096, 4097, 8192, 8193};
  
          for (int fragmentSize : fragmentSizes) {
              for (int dictionarySize : dictionarySizes) {
                  byte[] dictionary = new byte[dictionarySize];
                  for (int i = 0; i < dictionarySize; i++) {
                      byte b = (byte) i;
                      if (b == 0) b++; // no null bytes because they get trimmed
                      dictionary[i] = b;
                  }
  
                  System.out.printf("fragmentSize=%d dictionarySize=%d\n", fragmentSize, dictionarySize);
  
                  testReadDataTypeDictionaryBytes(ByteString.of(dictionary), fragmentSize);
              }
          }
      }
  
      @Test
      public void readBuiltinDataTypeDictionaryBytes() throws ExecutionException, InterruptedException {
          CompletableFuture<ByteString> future = dictionaryReader.readDataTypeDictionaryBytes(
              Identifiers.OpcUa_BinarySchema,
              Integer.MAX_VALUE // not used for built-in dictionary
          );
  
          assertNotNull(future.get());
      }
  
      private void testReadDataTypeDictionaryBytes(ByteString dictionary, int fragmentSize) throws Exception {
          Mockito
              .when(stackClient.sendRequest(ArgumentMatchers.any(ReadRequest.class)))
              .then(invocationOnMock -> {
                  ReadRequest readRequest = invocationOnMock.getArgument(0);
  
                  List<ReadValueId> readValueIds =
                      Arrays.stream(Objects.requireNonNull(readRequest.getNodesToRead()))
                          .collect(Collectors.toList());
  
                  ReadValueId readValueId = readValueIds.get(0);
  
                  NumericRange numericRange = NumericRange.parse(readValueId.getIndexRange());
  
                  try {
                      Object fragment = NumericRange.readFromValueAtRange(new Variant(dictionary), numericRange);
  
                      return completedFuture(new ReadResponse(
                          null,
                          new DataValue[]{new DataValue(new Variant(fragment))},
                          null
                      ));
                  } catch (UaException e) {
                      return completedFuture(new ReadResponse(
                          null,
                          new DataValue[]{new DataValue(e.getStatusCode())},
                          null
                      ));
                  }
              });
  
          ByteString typeDictionaryBs = dictionaryReader.readDataTypeDictionaryBytes(
              NodeId.NULL_VALUE,
              fragmentSize
          ).get();
  
          Assertions.assertEquals(typeDictionaryBs, dictionary);
      }
  
  }
  