    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.fileupload2.core;
    018
    019import static org.junit.jupiter.api.Assertions.assertEquals;
    020import static org.junit.jupiter.api.Assertions.assertTrue;
    021
    022import java.io.ByteArrayOutputStream;
    023import java.io.IOException;
    024import java.nio.charset.StandardCharsets;
    025
    026import org.junit.jupiter.api.Test;
    027
    028/**
    029 * Tests the {@link ProgressListener}.
    030 *
    031 * @param <AFU> The subclass of FileUpload.
    032 * @param <R>   The FileUpload request type.
    033 * @param <I>   The FileItem type.
    034 * @param <F>   The FileItemFactory type.
    035 */
    036public abstract class AbstractProgressListenerTest<AFU extends AbstractFileUpload<R, I, F>, R, I extends FileItem<I>, F extends FileItemFactory<I>>
    037        extends AbstractTest<AFU, R, I, F> {
    038
    039    protected static class ProgressListenerImpl implements ProgressListener {
    040
    041        private final long expectedContentLength;
    042
    043        private final int expectedItems;
    044
    045        private Long bytesRead;
    046
    047        private Integer items;
    048
    049        ProgressListenerImpl(final long contentLength, final int itemCount) {
    050            expectedContentLength = contentLength;
    051            expectedItems = itemCount;
    052        }
    053
    054        void checkFinished() {
    055            assertEquals(expectedContentLength, bytesRead.longValue());
    056            assertEquals(expectedItems, items.intValue());
    057        }
    058
    059        @Override
    060        public void update(final long actualBytesRead, final long actualContentLength, final int actualItems) {
    061            assertTrue(actualBytesRead >= 0 && actualBytesRead <= expectedContentLength);
    062            assertTrue(actualContentLength == -1 || actualContentLength == expectedContentLength);
    063            assertTrue(actualItems >= 0 && actualItems <= expectedItems);
    064
    065            assertTrue(bytesRead == null || actualBytesRead >= bytesRead.longValue());
    066            bytesRead = Long.valueOf(actualBytesRead);
    067            assertTrue(items == null || actualItems >= items.intValue());
    068            items = Integer.valueOf(actualItems);
    069        }
    070
    071    }
    072
    073    protected void runTest(final int itemCount, final long contentLength, final R request) throws FileUploadException, IOException {
    074        final var upload = newFileUpload();
    075        final var listener = new ProgressListenerImpl(contentLength, itemCount);
    076        upload.setProgressListener(listener);
    077        final var iter = upload.getItemIterator(request);
    078        for (var i = 0; i < itemCount; i++) {
    079            final var idxI = i;
    080            final var fileItemInput = iter.next();
    081            try (final var inputStream = fileItemInput.getInputStream()) {
    082                for (var j = 0; j < 16_384 + i; j++) {
    083                    final var idxJ = j;
    084                    //
    085                    // This used to be assertEquals((byte) j, (byte) istream.read()); but this seems to trigger a bug in JRockit, so we express the same like
    086                    // this:
    087                    //
    088                    final var b1 = (byte) j;
    089                    final var b2 = (byte) inputStream.read();
    090                    assertEquals(b1, b2, () -> String.format("itemCount = %,d, i = %,d, j = %,d", itemCount, idxI, idxJ));
    091                }
    092                assertEquals(-1, inputStream.read());
    093            }
    094        }
    095        assertTrue(!iter.hasNext());
    096        listener.checkFinished();
    097    }
    098
    099    /**
    100     * Parse a very long file upload by using a progress listener.
    101     *
    102     * @throws IOException Test failure.
    103     */
    104    @Test
    105    public void testProgressListener() throws IOException {
    106        final var numItems = 512;
    107        final var baos = new ByteArrayOutputStream();
    108        for (var i = 0; i < numItems; i++) {
    109            final var header = "-----1234\r\n" + "Content-Disposition: form-data; name=\"field" + (i + 1) + "\"\r\n" + "\r\n";
    110            baos.write(header.getBytes(StandardCharsets.US_ASCII));
    111            for (var j = 0; j < 16384 + i; j++) {
    112                baos.write((byte) j);
    113            }
    114            baos.write("\r\n".getBytes(StandardCharsets.US_ASCII));
    115        }
    116        baos.write("-----1234--\r\n".getBytes(StandardCharsets.US_ASCII));
    117        final var requestBytes = baos.toByteArray();
    118
    119        var request = newMockHttpServletRequest(requestBytes, null, Constants.CONTENT_TYPE, null);
    120        runTest(numItems, requestBytes.length, request);
    121        request = newMockHttpServletRequest(requestBytes, -1L, Constants.CONTENT_TYPE, null);
    122        runTest(numItems, requestBytes.length, request);
    123    }
    124
    125}