Response.java

1
package com.renomad.minum.web;
2
3
import java.io.ByteArrayOutputStream;
4
import java.io.IOException;
5
import java.io.RandomAccessFile;
6
import java.nio.ByteBuffer;
7
import java.nio.channels.FileChannel;
8
import java.nio.charset.StandardCharsets;
9
import java.nio.file.Files;
10
import java.nio.file.Path;
11
import java.util.Arrays;
12
import java.util.HashMap;
13
import java.util.Map;
14
import java.util.Objects;
15
import java.util.zip.GZIPOutputStream;
16
17
import static com.renomad.minum.utils.FileUtils.badFilePathPatterns;
18
import static com.renomad.minum.web.StatusLine.StatusCode.CODE_200_OK;
19
import static com.renomad.minum.web.StatusLine.StatusCode.CODE_206_PARTIAL_CONTENT;
20
21
/**
22
 * Represents an HTTP response. This is what will get sent back to the
23
 * client (that is, to the browser).  There are a variety of overloads
24
 * of this record for different situations.  The overarching paradigm is
25
 * to provide you high flexibility.
26
 * <p>
27
 * A response message is sent by a server to a client as a reply to its former request message.
28
 * </p>
29
 * <h3>
30
 * Response syntax
31
 * </h3>
32
 * <p>
33
 * A server sends response messages to the client, which consist of:
34
 * </p>
35
 * <ul>
36
 * <li>
37
 * a status line, consisting of the protocol version, a space, the
38
 * response status code, another space, a possibly empty reason
39
 * phrase, a carriage return and a line feed, e.g.:
40
 * <pre>
41
 * HTTP/1.1 200 OK
42
 * </pre>
43
 * </li>
44
 *
45
 * <li>
46
 * zero or more response header fields, each consisting of the case-insensitive
47
 * field name, a colon, optional leading whitespace, the field value, an
48
 * optional trailing whitespace and ending with a carriage return and a line feed, e.g.:
49
 * <pre>
50
 * Content-Type: text/html
51
 * </pre>
52
 * </li>
53
 *
54
 * <li>
55
 * an empty line, consisting of a carriage return and a line feed;
56
 * </li>
57
 *
58
 * <li>
59
 * an optional message body.
60
 * </li>
61
 *</ul>
62
63
 */
64
public final class Response implements IResponse {
65
66
    private final StatusLine.StatusCode statusCode;
67
    private final Map<String, String> extraHeaders;
68
    private final byte[] body;
69
    private final ThrowingConsumer<ISocketWrapper> outputGenerator;
70
    private final long bodyLength;
71
72
73
    /**
74
     * This is the constructor that provides access to all fields.  It is not intended
75
     * to be used from outside this class.
76
     * @param extraHeaders extra headers we want to return with the response.
77
     * @param outputGenerator a {@link ThrowingConsumer} that will use a {@link ISocketWrapper} parameter
78
     *                        to send bytes on the wire back to the client.  See the static factory methods
79
     *                        such as {@link #buildResponse(StatusLine.StatusCode, Map, byte[])} for more details on this.
80
     * @param bodyLength this is used to set the content-length header for the response.  If this is
81
     *                   not provided, we set the header to "transfer-encoding: chunked", or in other words, streaming.
82
     */
83
    Response(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, byte[] body,
84
             ThrowingConsumer<ISocketWrapper> outputGenerator, long bodyLength) {
85
        this.statusCode = statusCode;
86
        this.extraHeaders = new HashMap<>(extraHeaders);
87
        this.body = body;
88
        this.outputGenerator = outputGenerator;
89
        this.bodyLength = bodyLength;
90
    }
91
92
    /**
93
     * This factory method is intended for situations where the user wishes to stream data
94
     * but lacks the content length.  This is only for unusual situations where the developer
95
     * needs the extra control.  In most cases, other methods are more suitable.
96
     * @param extraHeaders any extra headers for the response, such as the content-type
97
     * @param outputGenerator a function that will be given a {@link ISocketWrapper}, providing the
98
     *                        ability to send bytes on the socket.
99
     */
100
    public static IResponse buildStreamingResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, ThrowingConsumer<ISocketWrapper> outputGenerator) {
101 1 1. buildStreamingResponse : replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED
        return new Response(statusCode, extraHeaders, null, outputGenerator, 0);
102
    }
103
104
    /**
105
     * Similar to {@link Response#buildStreamingResponse(StatusLine.StatusCode, Map, ThrowingConsumer)} but here we know
106
     * the body length, so that will be sent to the client as content-length.
107
     * @param extraHeaders any extra headers for the response, such as the content-type
108
     * @param outputGenerator a function that will be given a {@link ISocketWrapper}, providing the
109
     *                        ability to send bytes on the socket.
110
     * @param bodyLength the length, in bytes, of the data to be sent to the client
111
     */
112
    public static IResponse buildStreamingResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, ThrowingConsumer<ISocketWrapper> outputGenerator, long bodyLength) {
113 1 1. buildStreamingResponse : replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED
        return new Response(statusCode, extraHeaders, null, outputGenerator, bodyLength);
114
    }
115
116
    /**
117
     * A constructor for situations where the developer wishes to send a small (less than a megabyte) byte array
118
     * to the client.  If there is need to send something of larger size, choose one these
119
     * alternate constructors:
120
     * FileChannel - for sending a large file: {@link Response#buildLargeFileResponse(Map, String, Headers)}
121
     * Streaming - for more custom streaming control with a known body size: {@link Response#buildStreamingResponse(StatusLine.StatusCode, Map, ThrowingConsumer, long)}
122
     * Streaming - for more custom streaming control with body size unknown: {@link Response#buildStreamingResponse(StatusLine.StatusCode, Map, ThrowingConsumer)}
123
     *
124
     * @param extraHeaders any extra headers for the response, such as the content-type.
125
     */
126
    public static IResponse buildResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, byte[] body) {
127 2 1. lambda$buildResponse$0 : removed call to com/renomad/minum/web/Response::sendByteArrayResponse → KILLED
2. buildResponse : replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED
        return new Response(statusCode, extraHeaders, body, socketWrapper -> sendByteArrayResponse(socketWrapper, body), body.length);
128
    }
129
130
    /**
131
     * Build an ordinary response, with a known body
132
     * @param extraHeaders extra HTTP headers, like <pre>content-type: text/html</pre>
133
     */
134
    public static IResponse buildResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, String body) {
135
        byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
136 2 1. lambda$buildResponse$1 : removed call to com/renomad/minum/web/Response::sendByteArrayResponse → KILLED
2. buildResponse : replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED
        return new Response(statusCode, extraHeaders, bytes, socketWrapper -> sendByteArrayResponse(socketWrapper, bytes), bytes.length);
137
    }
138
139
    public static IResponse buildLargeFileResponse(Map<String, String> extraHeaders, String filePath, Headers requestHeaders) throws IOException {
140 1 1. buildLargeFileResponse : negated conditional → KILLED
        if (badFilePathPatterns.matcher(filePath).find()) {
141
            throw new WebServerException(String.format("Bad path requested at readFile: %s", filePath));
142
        }
143
144
        Map<String, String> adjustedHeaders = new HashMap<>(extraHeaders);
145
        long fileSize = Files.size(Path.of(filePath));
146
        var range = new Range(requestHeaders, fileSize);
147
        StatusLine.StatusCode responseCode = CODE_200_OK;
148
        long length = fileSize;
149 1 1. buildLargeFileResponse : negated conditional → TIMED_OUT
        if (range.hasRangeHeader()) {
150
            long offset = range.getOffset();
151
            length = range.getLength();
152 2 1. buildLargeFileResponse : Replaced long addition with subtraction → KILLED
2. buildLargeFileResponse : Replaced long subtraction with addition → KILLED
            var lastIndex = (offset + length) - 1;
153
            adjustedHeaders.put("Content-Range", String.format("bytes %d-%d/%d", offset, lastIndex, fileSize));
154
            responseCode = CODE_206_PARTIAL_CONTENT;
155
        }
156
157
        ThrowingConsumer<ISocketWrapper> outputGenerator = socketWrapper -> {
158
            try (RandomAccessFile reader = new RandomAccessFile(filePath, "r")) {
159 1 1. lambda$buildLargeFileResponse$2 : removed call to java/io/RandomAccessFile::seek → SURVIVED
                reader.seek(range.getOffset());
160
                var fileChannel = reader.getChannel();
161 1 1. lambda$buildLargeFileResponse$2 : removed call to com/renomad/minum/web/Response::sendFileChannelResponse → TIMED_OUT
                sendFileChannelResponse(socketWrapper, fileChannel, range.getLength());
162
            }
163
        };
164
165 1 1. buildLargeFileResponse : replaced return value with null for com/renomad/minum/web/Response::buildLargeFileResponse → KILLED
        return new Response(responseCode, adjustedHeaders, null, outputGenerator, length);
166
    }
167
168
    /**
169
     * Build a {@link Response} with just a status code and headers, without a body
170
     * @param extraHeaders extra HTTP headers
171
     */
172
    public static IResponse buildLeanResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders) {
173 1 1. buildLeanResponse : replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED
        return new Response(statusCode, extraHeaders, null, socketWrapper -> {}, 0);
174
    }
175
176
    /**
177
     * Build a {@link Response} with only a status code, with no body and no extra headers.
178
     */
179
    public static IResponse buildLeanResponse(StatusLine.StatusCode statusCode) {
180 1 1. buildLeanResponse : replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED
        return new Response(statusCode, Map.of(), null, socketWrapper -> {}, 0);
181
    }
182
183
184
    /**
185
     * A helper method to create a response that returns a
186
     * 303 status code ("see other").  Provide a url that will
187
     * be handed to the browser.  This url may be relative or absolute.
188
     */
189
    public static IResponse redirectTo(String locationUrl) {
190 1 1. redirectTo : replaced return value with null for com/renomad/minum/web/Response::redirectTo → KILLED
        return buildLeanResponse(StatusLine.StatusCode.CODE_303_SEE_OTHER, Map.of("location", locationUrl));
191
    }
192
193
    /**
194
     * If you are returning HTML text with a 200 ok, this is a helper that
195
     * lets you skip some of the boilerplate. This version of the helper
196
     * lets you add extra headers on top of the basic content-type headers
197
     * that are needed to specify this is HTML.
198
     */
199
    public static IResponse htmlOk(String body, Map<String, String> extraHeaders) {
200
        var headers = new HashMap<String, String>();
201
        headers.put("Content-Type", "text/html; charset=UTF-8");
202 1 1. htmlOk : removed call to java/util/HashMap::putAll → KILLED
        headers.putAll(extraHeaders);
203 1 1. htmlOk : replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED
        return buildResponse(StatusLine.StatusCode.CODE_200_OK, headers, body);
204
    }
205
206
    /**
207
     * If you are returning HTML text with a 200 ok, this is a helper that
208
     * lets you skip some of the boilerplate.
209
     */
210
    public static IResponse htmlOk(String body) {
211 1 1. htmlOk : replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED
        return htmlOk(body, Map.of());
212
    }
213
214
    @Override
215
    public Map<String, String> getExtraHeaders() {
216 1 1. getExtraHeaders : replaced return value with Collections.emptyMap for com/renomad/minum/web/Response::getExtraHeaders → KILLED
        return new HashMap<>(extraHeaders);
217
    }
218
219
    @Override
220
    public StatusLine.StatusCode getStatusCode() {
221 1 1. getStatusCode : replaced return value with null for com/renomad/minum/web/Response::getStatusCode → KILLED
        return statusCode;
222
    }
223
224
    /**
225
     * Gets the length of the body for this response.  If the body
226
     * is an array of bytes set by the user, we grab this value by the
227
     * length() method.  If the outgoing data is set by a lambda, the user
228
     * will set the bodyLength value.
229
     */
230
    long getBodyLength() {
231 1 1. getBodyLength : negated conditional → KILLED
        if (body != null) {
232 1 1. getBodyLength : replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED
            return body.length;
233
        } else {
234 1 1. getBodyLength : replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED
            return bodyLength;
235
        }
236
    }
237
238
    /**
239
     * By calling this method with a {@link ISocketWrapper} parameter, the method
240
     * will send bytes on the associated socket.
241
     */
242
    void sendBody(ISocketWrapper sw) throws IOException {
243
        try {
244 1 1. sendBody : removed call to com/renomad/minum/web/ThrowingConsumer::accept → KILLED
            outputGenerator.accept(sw);
245
        } catch (Exception ex) {
246
            throw new IOException(ex.getMessage(), ex);
247
        }
248
    }
249
250
    /**
251
     * put bytes from a file into the socket, sending to the client
252
     * @param fileChannel the file we are reading from, based on a {@link RandomAccessFile}
253
     * @param length the number of bytes to send.  May be less than the full length of this {@link FileChannel}
254
     */
255
    private static void sendFileChannelResponse(ISocketWrapper sw, FileChannel fileChannel, long length) throws IOException {
256
        try {
257
            int bufferSize = 8 * 1024;
258
            ByteBuffer buff = ByteBuffer.allocate(bufferSize);
259
            long countBytesLeftToSend = length;
260
            while (true) {
261
                int countBytesRead = fileChannel.read(buff);
262 2 1. sendFileChannelResponse : changed conditional boundary → TIMED_OUT
2. sendFileChannelResponse : negated conditional → KILLED
                if (countBytesRead <= 0) {
263
                    break;
264
                } else {
265 2 1. sendFileChannelResponse : changed conditional boundary → SURVIVED
2. sendFileChannelResponse : negated conditional → KILLED
                    if (countBytesLeftToSend < countBytesRead) {
266 1 1. sendFileChannelResponse : removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED
                        sw.send(buff.array(), 0, (int)countBytesLeftToSend);
267
                        break;
268
                    } else {
269 1 1. sendFileChannelResponse : removed call to com/renomad/minum/web/ISocketWrapper::send → TIMED_OUT
                        sw.send(buff.array(), 0, countBytesRead);
270
                    }
271
                    buff.clear();
272
                }
273 1 1. sendFileChannelResponse : Replaced long subtraction with addition → SURVIVED
                countBytesLeftToSend -= countBytesRead;
274
            }
275
        } finally {
276 1 1. sendFileChannelResponse : removed call to java/nio/channels/FileChannel::close → KILLED
            fileChannel.close();
277
        }
278
    }
279
280
    private static void sendByteArrayResponse(ISocketWrapper sw, byte[] body) throws IOException {
281 1 1. sendByteArrayResponse : removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED
        sw.send(body);
282
    }
283
284
    @Override
285
    public byte[] getBody() {
286 1 1. getBody : replaced return value with null for com/renomad/minum/web/Response::getBody → KILLED
        return body;
287
    }
288
289
    /**
290
     * Compress the data in this body using gzip.
291
     * <br>
292
     * This operates by getting the body field from this instance of {@link Response} and
293
     * creating a new Response with the compressed data.
294
     */
295
    Response compressBody() throws IOException {
296
297
        ByteArrayOutputStream out = new ByteArrayOutputStream();
298
        var gos = new GZIPOutputStream(out);
299 1 1. compressBody : removed call to java/util/zip/GZIPOutputStream::write → SURVIVED
        gos.write(body);
300 1 1. compressBody : removed call to java/util/zip/GZIPOutputStream::finish → SURVIVED
        gos.finish();
301 1 1. compressBody : replaced return value with null for com/renomad/minum/web/Response::compressBody → KILLED
        return (Response)Response.buildResponse(
302
                statusCode,
303
                extraHeaders,
304
                out.toByteArray()
305
        );
306
    }
307
308
    @Override
309
    public boolean equals(Object o) {
310 2 1. equals : replaced boolean return with false for com/renomad/minum/web/Response::equals → KILLED
2. equals : negated conditional → KILLED
        if (this == o) return true;
311 3 1. equals : negated conditional → KILLED
2. equals : replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED
3. equals : negated conditional → KILLED
        if (o == null || getClass() != o.getClass()) return false;
312
        Response response = (Response) o;
313 5 1. equals : negated conditional → KILLED
2. equals : replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED
3. equals : negated conditional → KILLED
4. equals : negated conditional → KILLED
5. equals : negated conditional → KILLED
        return bodyLength == response.bodyLength && statusCode == response.statusCode && Objects.equals(extraHeaders, response.extraHeaders) && Arrays.equals(body, response.body);
314
    }
315
316
    @Override
317
    public int hashCode() {
318
        int result = Objects.hash(statusCode, extraHeaders, bodyLength);
319 2 1. hashCode : Replaced integer addition with subtraction → SURVIVED
2. hashCode : Replaced integer multiplication with division → KILLED
        result = 31 * result + Arrays.hashCode(body);
320 1 1. hashCode : replaced int return with 0 for com/renomad/minum/web/Response::hashCode → KILLED
        return result;
321
    }
322
323
    @Override
324
    public String toString() {
325 1 1. toString : replaced return value with "" for com/renomad/minum/web/Response::toString → KILLED
        return "Response{" +
326
                "statusCode=" + statusCode +
327
                ", extraHeaders=" + extraHeaders +
328
                ", bodyLength=" + bodyLength +
329
                '}';
330
    }
331
}

Mutations

101

1.1
Location : buildStreamingResponse
Killed by : com.renomad.minum.web.ResponseTests.testResponse_Streaming(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED

113

1.1
Location : buildStreamingResponse
Killed by : com.renomad.minum.web.WebTests
replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED

127

1.1
Location : lambda$buildResponse$0
Killed by : com.renomad.minum.FunctionalTests
removed call to com/renomad/minum/web/Response::sendByteArrayResponse → KILLED

2.2
Location : buildResponse
Killed by : com.renomad.minum.web.WebFrameworkTests.test_compressIfRequested(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED

136

1.1
Location : lambda$buildResponse$1
Killed by : com.renomad.minum.web.ResponseTests.testResponse_EdgeCase_SendBodyWithException(com.renomad.minum.web.ResponseTests)
removed call to com/renomad/minum/web/Response::sendByteArrayResponse → KILLED

2.2
Location : buildResponse
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED

140

1.1
Location : buildLargeFileResponse
Killed by : com.renomad.minum.web.ResponseTests.testResponse_EdgeCase_BadPathRequested(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

149

1.1
Location : buildLargeFileResponse
Killed by : none
negated conditional → TIMED_OUT

152

1.1
Location : buildLargeFileResponse
Killed by : com.renomad.minum.FunctionalTests
Replaced long addition with subtraction → KILLED

2.2
Location : buildLargeFileResponse
Killed by : com.renomad.minum.FunctionalTests
Replaced long subtraction with addition → KILLED

159

1.1
Location : lambda$buildLargeFileResponse$2
Killed by : none
removed call to java/io/RandomAccessFile::seek → SURVIVED
Covering tests

161

1.1
Location : lambda$buildLargeFileResponse$2
Killed by : none
removed call to com/renomad/minum/web/Response::sendFileChannelResponse → TIMED_OUT

165

1.1
Location : buildLargeFileResponse
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Response::buildLargeFileResponse → KILLED

173

1.1
Location : buildLeanResponse
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED

180

1.1
Location : buildLeanResponse
Killed by : com.renomad.minum.web.WebFrameworkTests.test_readStaticFile_Edge_OutsideDirectory(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED

190

1.1
Location : redirectTo
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Response::redirectTo → KILLED

202

1.1
Location : htmlOk
Killed by : com.renomad.minum.web.WebTests
removed call to java/util/HashMap::putAll → KILLED

203

1.1
Location : htmlOk
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED

211

1.1
Location : htmlOk
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED

216

1.1
Location : getExtraHeaders
Killed by : com.renomad.minum.web.WebFrameworkTests.test_Edge_ApplicationOctetStream(com.renomad.minum.web.WebFrameworkTests)
replaced return value with Collections.emptyMap for com/renomad/minum/web/Response::getExtraHeaders → KILLED

221

1.1
Location : getStatusCode
Killed by : com.renomad.minum.web.WebFrameworkTests.test_readStaticFile_Edge_OutsideDirectory(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::getStatusCode → KILLED

231

1.1
Location : getBodyLength
Killed by : com.renomad.minum.web.WebTests
negated conditional → KILLED

232

1.1
Location : getBodyLength
Killed by : com.renomad.minum.web.WebFrameworkTests.test_compressIfRequested(com.renomad.minum.web.WebFrameworkTests)
replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED

234

1.1
Location : getBodyLength
Killed by : com.renomad.minum.web.WebTests
replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED

244

1.1
Location : sendBody
Killed by : com.renomad.minum.web.ResponseTests.testResponse_Streaming(com.renomad.minum.web.ResponseTests)
removed call to com/renomad/minum/web/ThrowingConsumer::accept → KILLED

262

1.1
Location : sendFileChannelResponse
Killed by : none
changed conditional boundary → TIMED_OUT

2.2
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
negated conditional → KILLED

265

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
negated conditional → KILLED

2.2
Location : sendFileChannelResponse
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

266

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED

269

1.1
Location : sendFileChannelResponse
Killed by : none
removed call to com/renomad/minum/web/ISocketWrapper::send → TIMED_OUT

273

1.1
Location : sendFileChannelResponse
Killed by : none
Replaced long subtraction with addition → SURVIVED
Covering tests

276

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
removed call to java/nio/channels/FileChannel::close → KILLED

281

1.1
Location : sendByteArrayResponse
Killed by : com.renomad.minum.web.ResponseTests.testResponse_EdgeCase_SendBodyWithException(com.renomad.minum.web.ResponseTests)
removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED

286

1.1
Location : getBody
Killed by : com.renomad.minum.web.EndpointTests.test_Endpoint_HappyPath(com.renomad.minum.web.EndpointTests)
replaced return value with null for com/renomad/minum/web/Response::getBody → KILLED

299

1.1
Location : compressBody
Killed by : none
removed call to java/util/zip/GZIPOutputStream::write → SURVIVED
Covering tests

300

1.1
Location : compressBody
Killed by : none
removed call to java/util/zip/GZIPOutputStream::finish → SURVIVED
Covering tests

301

1.1
Location : compressBody
Killed by : com.renomad.minum.web.WebFrameworkTests.test_compressIfRequested(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::compressBody → KILLED

310

1.1
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with false for com/renomad/minum/web/Response::equals → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

311

1.1
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED

3.3
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

313

1.1
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED

3.3
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

4.4
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

5.5
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

319

1.1
Location : hashCode
Killed by : none
Replaced integer addition with subtraction → SURVIVED
Covering tests

2.2
Location : hashCode
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
Replaced integer multiplication with division → KILLED

320

1.1
Location : hashCode
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced int return with 0 for com/renomad/minum/web/Response::hashCode → KILLED

325

1.1
Location : toString
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with "" for com/renomad/minum/web/Response::toString → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0