@VisibleForTesting
  static LocalImage cacheDockerImageTar(
      BuildContext buildContext,
      Path tarPath,
      ProgressEventDispatcher.Factory progressEventDispatcherFactory,
      TempDirectoryProvider tempDirectoryProvider)
      throws IOException, LayerCountMismatchException {
    ExecutorService executorService = buildContext.getExecutorService();
    Path destination = tempDirectoryProvider.newDirectory();

    try (TimerEventDispatcher ignored =
        new TimerEventDispatcher(
            buildContext.getEventHandlers(),
            "Extracting tar " + tarPath + " into " + destination)) {
      TarExtractor.extract(tarPath, destination);

      DockerManifestEntryTemplate loadManifest;
      try (InputStream manifestStream =
          Files.newInputStream(destination.resolve("manifest.json"))) {
        loadManifest =
            JsonMapper.builder()
                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
                .build()
                .readValue(manifestStream, DockerManifestEntryTemplate[].class)[0];
      }

      Path configPath = destination.resolve(loadManifest.getConfig());
      ContainerConfigurationTemplate configurationTemplate =
          JsonTemplateMapper.readJsonFromFile(configPath, ContainerConfigurationTemplate.class);
      // Don't compute the digest of the loaded Java JSON instance.
      BlobDescriptor originalConfigDescriptor =
          Blobs.from(configPath).writeTo(ByteStreams.nullOutputStream());

      List<String> layerFiles = loadManifest.getLayerFiles();
      if (configurationTemplate.getLayerCount() != layerFiles.size()) {
        throw new LayerCountMismatchException(
            "Invalid base image format: manifest contains "
                + layerFiles.size()
                + " layers, but container configuration contains "
                + configurationTemplate.getLayerCount()
                + " layers");
      }
      buildContext
          .getBaseImageLayersCache()
          .writeLocalConfig(originalConfigDescriptor.getDigest(), configurationTemplate);

      // Check the first layer to see if the layers are compressed already. 'docker save' output
      // is uncompressed, but a jib-built tar has compressed layers.
      boolean layersAreCompressed =
          !layerFiles.isEmpty() && isGzipped(destination.resolve(layerFiles.get(0)));

      // Process layer blobs
      try (ProgressEventDispatcher progressEventDispatcher =
          progressEventDispatcherFactory.create(
              "processing base image layers", layerFiles.size())) {
        // Start compressing layers in parallel
        List<Future<PreparedLayer>> preparedLayers = new ArrayList<>();
        for (int index = 0; index < layerFiles.size(); index++) {
          Path layerFile = destination.resolve(layerFiles.get(index));
          DescriptorDigest diffId = configurationTemplate.getLayerDiffId(index);
          ProgressEventDispatcher.Factory layerProgressDispatcherFactory =
              progressEventDispatcher.newChildProducer();
          preparedLayers.add(
              executorService.submit(
                  () ->
                      compressAndCacheTarLayer(
                          buildContext.getBaseImageLayersCache(),
                          diffId,
                          layerFile,
                          layersAreCompressed,
                          layerProgressDispatcherFactory)));
        }
        return new LocalImage(preparedLayers, configurationTemplate);
      }
    }
  }
@Test
  public void testCacheDockerImageTar_validDocker() throws Exception {
    Path dockerBuild = getResource("core/extraction/docker-save.tar");
    LocalImage result =
        LocalBaseImageSteps.cacheDockerImageTar(
            buildContext, dockerBuild, progressEventDispatcherFactory, tempDirectoryProvider);

    Mockito.verify(progressEventDispatcher, Mockito.times(2)).newChildProducer();
    Assert.assertEquals(2, result.layers.size());
    Assert.assertEquals(
        "5e701122d3347fae0758cd5b7f0692c686fcd07b0e7fd9c4a125fbdbbedc04dd",
        result.layers.get(0).get().getDiffId().getHash());
    Assert.assertEquals(
        "0011328ac5dfe3dde40c7c5e0e00c98d1833a3aeae2bfb668cf9eb965c229c7f",
        result.layers.get(0).get().getBlobDescriptor().getDigest().getHash());
    Assert.assertEquals(
        "f1ac3015bcbf0ada4750d728626eb10f0f585199e2b667dcd79e49f0e926178e",
        result.layers.get(1).get().getDiffId().getHash());
    Assert.assertEquals(
        "c10ef24a5cef5092bbcb5a5666721cff7b86ce978c203a958d1fc86ee6c19f94",
        result.layers.get(1).get().getBlobDescriptor().getDigest().getHash());
    Assert.assertEquals(2, result.configurationTemplate.getLayerCount());
  }