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}