/**
   * Make sure when seeding a given zoom level, the correct tiles are sent to the {@link
   * StorageBroker}
   *
   * @throws Exception
   */
  @SuppressWarnings("serial")
  public void testSeedStoredTiles() throws Exception {

    WMSLayer tl = createWMSLayer("image/png");

    // create an image to be returned by the mock WMSSourceHelper
    // / final byte[] fakeWMSResponse = createFakeSourceImage(tl);
    // WMSSourceHelper that on makeRequest() returns always the saqme fake image
    WMSSourceHelper mockSourceHelper =
        new MockWMSSourceHelper(); // EasyMock.createMock(WMSSourceHelper.class);
    // expect(mockSourceHelper.makeRequest((WMSMetaTile)
    // anyObject())).andReturn(fakeWMSResponse)
    // .anyTimes();
    // replay(mockSourceHelper);
    tl.setSourceHelper(mockSourceHelper);

    final String gridSetId = tl.getGridSubsets().iterator().next();
    final int zoomLevel = 2;
    SeedRequest req = createRequest(tl, TYPE.SEED, zoomLevel, zoomLevel);

    /*
     * Create a mock storage broker that has never an image in its blob store and that captures
     * the TileObject the seeder requests it to store for further test validation
     */
    final StorageBroker mockStorageBroker = EasyMock.createMock(StorageBroker.class);
    Capture<TileObject> storedObjects =
        new Capture<TileObject>() {
          /** Override because setValue with anyTimes() resets the list of values */
          @Override
          public void setValue(TileObject o) {
            super.getValues().add(o);
          }
        };
    expect(mockStorageBroker.put(capture(storedObjects))).andReturn(true).anyTimes();
    expect(mockStorageBroker.get((TileObject) anyObject())).andReturn(false).anyTimes();
    replay(mockStorageBroker);

    TileRange tr = TileBreeder.createTileRange(req, tl);
    TileRangeIterator trIter = new TileRangeIterator(tr, tl.getMetaTilingFactors());

    boolean reseed = false;
    SeedTask task = new SeedTask(mockStorageBroker, trIter, tl, reseed, false);
    task.setTaskId(1L);
    task.setThreadInfo(new AtomicInteger(), 0);
    /*
     * HACK: avoid SeedTask.getCurrentThreadArrayIndex failure.
     */
    Thread.currentThread().setName("pool-fake-thread-1");

    /*
     * Call the seed process
     */
    task.doAction();

    final GridSubset gridSubset = tl.getGridSubset(gridSetId);

    /*
     * Make sure the seed process asked for the expected tiles to be stored
     */
    final long expectedSavedTileCount;

    final long[] coveredGridLevels = gridSubset.getCoverage(zoomLevel);

    // seeding should not include edge tiles produced by the meta tiling that don't fall into
    // the gridsubset's coverage
    long starty = coveredGridLevels[1];
    long startx = coveredGridLevels[0];

    expectedSavedTileCount =
        (coveredGridLevels[2] - startx + 1) * (coveredGridLevels[3] - starty + 1);

    List<TileObject> storedTiles = storedObjects.getValues();
    final int seededTileCount = storedTiles.size();

    assertEquals(expectedSavedTileCount, seededTileCount);

    Set<Tuple<Long>> tileKeys = new TreeSet<Tuple<Long>>();
    Set<Tuple<Long>> expectedTiles = new TreeSet<Tuple<Long>>();
    for (long x = startx; x <= coveredGridLevels[2]; x++) {
      for (long y = starty; y <= coveredGridLevels[3]; y++) {
        expectedTiles.add(new Tuple<Long>(x, y, (long) zoomLevel));
      }
    }
    for (TileObject obj : storedTiles) {
      tileKeys.add(new Tuple<Long>(obj.getXYZ()[0], obj.getXYZ()[1], obj.getXYZ()[2]));
    }

    assertEquals(expectedTiles, tileKeys);
  }
  /**
   * For a metatiled seed request over a given zoom level, make sure the correct wms calls are
   * issued
   *
   * @throws Exception
   */
  @SuppressWarnings("serial")
  public void testSeedWMSRequests() throws Exception {
    WMSLayer tl = createWMSLayer("image/png");

    // create an image to be returned by the mock WMSSourceHelper
    final byte[] fakeWMSResponse = createFakeSourceImage(tl);

    // WMSSourceHelper that on makeRequest() returns always the saqme fake image
    WMSSourceHelper mockSourceHelper = EasyMock.createMock(WMSSourceHelper.class);

    final AtomicInteger wmsRequestsCounter = new AtomicInteger();
    Capture<WMSMetaTile> wmsRequestsCapturer =
        new Capture<WMSMetaTile>() {
          /** Override because setValue with anyTimes() resets the list of values */
          @Override
          public void setValue(WMSMetaTile o) {
            wmsRequestsCounter.incrementAndGet();
          }
        };
    Capture<Resource> resourceCapturer =
        new Capture<Resource>() {
          @Override
          public void setValue(Resource target) {
            try {
              target.transferFrom(Channels.newChannel(new ByteArrayInputStream(fakeWMSResponse)));
            } catch (IOException e) {
              throw new RuntimeException(e);
            }
          }
        };
    mockSourceHelper.makeRequest(capture(wmsRequestsCapturer), capture(resourceCapturer));
    mockSourceHelper.makeRequest(capture(wmsRequestsCapturer), capture(resourceCapturer));
    mockSourceHelper.makeRequest(capture(wmsRequestsCapturer), capture(resourceCapturer));
    mockSourceHelper.setConcurrency(32);
    mockSourceHelper.setBackendTimeout(120);
    replay(mockSourceHelper);

    tl.setSourceHelper(mockSourceHelper);

    final int zoomLevel = 4;
    SeedRequest req = createRequest(tl, TYPE.SEED, zoomLevel, zoomLevel);

    TileRange tr = TileBreeder.createTileRange(req, tl);
    TileRangeIterator trIter = new TileRangeIterator(tr, tl.getMetaTilingFactors());

    /*
     * Create a mock storage broker that does nothing
     */
    final StorageBroker mockStorageBroker = EasyMock.createMock(StorageBroker.class);
    expect(mockStorageBroker.put((TileObject) anyObject())).andReturn(true).anyTimes();
    expect(mockStorageBroker.get((TileObject) anyObject())).andReturn(false).anyTimes();
    replay(mockStorageBroker);

    boolean reseed = false;
    SeedTask seedTask = new SeedTask(mockStorageBroker, trIter, tl, reseed, false);
    seedTask.setTaskId(1L);
    seedTask.setThreadInfo(new AtomicInteger(), 0);
    /*
     * HACK: avoid SeedTask.getCurrentThreadArrayIndex failure.
     */
    Thread.currentThread().setName("pool-fake-thread-1");

    /*
     * Call the seed process
     */
    seedTask.doAction();

    final long expectedWmsRequestsCount = 3; // due to metatiling
    final long wmsRequestCount = wmsRequestsCounter.get();
    assertEquals(expectedWmsRequestsCount, wmsRequestCount);
  }
  /**
   * For a metatiled seed request over a given zoom level, make sure the correct wms calls are
   * issued
   *
   * @throws Exception
   */
  public void testSeedRetries() throws Exception {
    WMSLayer tl = createWMSLayer("image/png");

    // create an image to be returned by the mock WMSSourceHelper
    final byte[] fakeWMSResponse = createFakeSourceImage(tl);

    // WMSSourceHelper that on makeRequest() returns always the saqme fake image
    // WMSSourceHelper mockSourceHelper = new MockWMSSourceHelper();///
    // EasyMock.createMock(WMSSourceHelper.class);
    WMSSourceHelper mockSourceHelper =
        new MockWMSSourceHelper() {
          private int numCalls;

          @Override
          protected void makeRequest(
              TileResponseReceiver tileRespRecv,
              WMSLayer layer,
              Map<String, String> wmsParams,
              MimeType expectedMimeType,
              Resource target)
              throws GeoWebCacheException {
            numCalls++;
            switch (numCalls) {
              case 1:
                throw new GeoWebCacheException("test exception");
              case 2:
                throw new RuntimeException("test unexpected exception");
              case 3:
                throw new GeoWebCacheException("second test exception");
              case 4:
                throw new RuntimeException("second test unexpected exception");
              default:
                try {
                  target.transferFrom(
                      Channels.newChannel(new ByteArrayInputStream(fakeWMSResponse)));
                } catch (IOException e) {
                  throw new RuntimeException(e);
                }
            }
          }
        };

    tl.setSourceHelper(mockSourceHelper);

    final int zoomLevel = 4;
    SeedRequest req = createRequest(tl, TYPE.SEED, zoomLevel, zoomLevel);

    TileRange tr = TileBreeder.createTileRange(req, tl);
    TileRangeIterator trIter = new TileRangeIterator(tr, tl.getMetaTilingFactors());

    /*
     * Create a mock storage broker that does nothing
     */
    final StorageBroker mockStorageBroker = EasyMock.createMock(StorageBroker.class);
    expect(mockStorageBroker.put((TileObject) anyObject())).andReturn(true).anyTimes();
    expect(mockStorageBroker.get((TileObject) anyObject())).andReturn(false).anyTimes();
    replay(mockStorageBroker);

    boolean reseed = false;
    SeedTask seedTask = new SeedTask(mockStorageBroker, trIter, tl, reseed, false);
    seedTask.setTaskId(1L);
    seedTask.setThreadInfo(new AtomicInteger(), 0);

    int tileFailureRetryCount = 1;
    long tileFailureRetryWaitTime = 10;
    long totalFailuresBeforeAborting = 4;
    AtomicLong sharedFailureCounter = new AtomicLong();
    seedTask.setFailurePolicy(
        tileFailureRetryCount,
        tileFailureRetryWaitTime,
        totalFailuresBeforeAborting,
        sharedFailureCounter);
    /*
     * HACK: avoid SeedTask.getCurrentThreadArrayIndex failure.
     */
    Thread.currentThread().setName("pool-fake-thread-1");

    /*
     * Call the seed process
     */
    seedTask.doAction();
    assertEquals(totalFailuresBeforeAborting, sharedFailureCounter.get());
  }