public static void checkChunkAndPage(
      Map<String, List<List<Long>>> chunkPagePointsNum, TsFileResource mergedFile)
      throws IOException, IllegalPathException {
    Map<String, List<List<Long>>> mergedChunkPagePointsNum = new HashMap<>();
    List<Long> pagePointsNum = new ArrayList<>();
    try (TsFileSequenceReader reader = new TsFileSequenceReader(mergedFile.getTsFilePath())) {
      IDeviceID entity = IDeviceID.Factory.DEFAULT_FACTORY.create("");
      String measurement = "";
      // Sequential reading of one ChunkGroup now follows this order:
      // first the CHUNK_GROUP_HEADER, then SeriesChunks (headers and data) in one ChunkGroup
      // Because we do not know how many chunks a ChunkGroup may have, we should read one byte (the
      // marker) ahead and judge accordingly.
      reader.position((long) TSFileConfig.MAGIC_STRING.getBytes().length + 1);
      byte marker;
      while ((marker = reader.readMarker()) != MetaMarker.SEPARATOR) {
        switch (marker) {
          case MetaMarker.CHUNK_HEADER:
          case MetaMarker.TIME_CHUNK_HEADER:
          case MetaMarker.VALUE_CHUNK_HEADER:
          case MetaMarker.ONLY_ONE_PAGE_CHUNK_HEADER:
          case MetaMarker.ONLY_ONE_PAGE_TIME_CHUNK_HEADER:
          case MetaMarker.ONLY_ONE_PAGE_VALUE_CHUNK_HEADER:
            ChunkHeader header = reader.readChunkHeader(marker);
            // read the next measurement and pack data of last measurement
            if (pagePointsNum.size() > 0) {
              String fullPath = new MeasurementPath(entity, measurement).getFullPath();
              List<List<Long>> currChunkPagePointsNum =
                  mergedChunkPagePointsNum.computeIfAbsent(fullPath, k -> new ArrayList<>());
              currChunkPagePointsNum.add(pagePointsNum);
              pagePointsNum = new ArrayList<>();
            }
            measurement = header.getMeasurementID();
            Decoder defaultTimeDecoder =
                Decoder.getDecoderByType(
                    TSEncoding.valueOf(TSFileDescriptor.getInstance().getConfig().getTimeEncoder()),
                    TSDataType.INT64);
            Decoder valueDecoder =
                Decoder.getDecoderByType(header.getEncodingType(), header.getDataType());
            int dataSize = header.getDataSize();
            while (dataSize > 0) {
              valueDecoder.reset();
              PageHeader pageHeader =
                  reader.readPageHeader(
                      header.getDataType(), header.getChunkType() == MetaMarker.CHUNK_HEADER);
              ByteBuffer pageData = reader.readPage(pageHeader, header.getCompressionType());
              PageReader reader1 =
                  new PageReader(pageData, header.getDataType(), valueDecoder, defaultTimeDecoder);
              BatchData batchData = reader1.getAllSatisfiedPageData();
              if (header.getChunkType() == MetaMarker.CHUNK_HEADER) {
                pagePointsNum.add(pageHeader.getNumOfValues());
              } else {
                pagePointsNum.add((long) batchData.length());
              }
              IBatchDataIterator batchDataIterator = batchData.getBatchDataIterator();
              while (batchDataIterator.hasNext()) {
                batchDataIterator.next();
              }
              dataSize -= pageHeader.getSerializedPageSize();
            }
            break;
          case MetaMarker.CHUNK_GROUP_HEADER:
            ChunkGroupHeader chunkGroupHeader = reader.readChunkGroupHeader();
            // read the next measurement and pack data of last device
            if (pagePointsNum.size() > 0) {
              String fullPath = new MeasurementPath(entity, measurement).getFullPath();
              List<List<Long>> currChunkPagePointsNum =
                  mergedChunkPagePointsNum.computeIfAbsent(fullPath, k -> new ArrayList<>());
              currChunkPagePointsNum.add(pagePointsNum);
              pagePointsNum = new ArrayList<>();
            }
            entity = chunkGroupHeader.getDeviceID();
            break;
          case MetaMarker.OPERATION_INDEX_RANGE:
            reader.readPlanIndex();
            break;
          default:
            MetaMarker.handleUnexpectedMarker(marker);
        }
      }
      // pack data of last measurement
      if (pagePointsNum.size() > 0) {
        String fullPath = new MeasurementPath(entity, measurement).getFullPath();
        List<List<Long>> currChunkPagePointsNum =
            mergedChunkPagePointsNum.computeIfAbsent(fullPath, k -> new ArrayList<>());
        currChunkPagePointsNum.add(pagePointsNum);
      }

      for (Entry<String, List<List<Long>>> chunkPagePointsNumEntry :
          chunkPagePointsNum.entrySet()) {
        String fullPath = chunkPagePointsNumEntry.getKey();
        List<List<Long>> sourceChunkPages = chunkPagePointsNumEntry.getValue();
        List<List<Long>> mergedChunkPages = mergedChunkPagePointsNum.get(fullPath);
        if (sourceChunkPages == null) {
          assertNull(mergedChunkPages);
          continue;
        }
        for (int i = 0; i < sourceChunkPages.size(); i++) {
          for (int j = 0; j < sourceChunkPages.get(i).size(); j++) {
            assertEquals(sourceChunkPages.get(i).get(j), mergedChunkPages.get(i).get(j));
          }
        }
      }
    }
  }
Use TsFileSequenceReader to read the merged file from the front to the back, and compare it
with the data before the merge one by one to ensure that the order and size of chunk and page
are one-to-one correspondence

@param chunkPagePointsNum Target chunk and page size, fullPath ->
    ChunkList(PageList(pointNumInEachPage))
@param mergedFile The merged File to be checked
iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/compaction/utils/CompactionCheckerUtils.java