• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / #161

21 Aug 2024 12:09AM UTC coverage: 80.582% (-0.04%) from 80.622%
#161

push

github

hazendaz
[servlet] Add support for servlet 6.1.0

3231 of 4121 branches covered (78.4%)

Branch coverage included in aggregate %.

0 of 5 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

8285 of 10170 relevant lines covered (81.47%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.13
/src/main/java/com/meterware/servletunit/ServletUnitHttpResponse.java
1
/*
2
 * MIT License
3
 *
4
 * Copyright 2011-2024 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.servletunit;
21

22
import com.meterware.httpunit.HttpUnitUtils;
23

24
import jakarta.servlet.ServletOutputStream;
25
import jakarta.servlet.WriteListener;
26
import jakarta.servlet.http.Cookie;
27
import jakarta.servlet.http.HttpServletResponse;
28

29
import java.io.ByteArrayOutputStream;
30
import java.io.IOException;
31
import java.io.OutputStreamWriter;
32
import java.io.PrintWriter;
33
import java.io.UnsupportedEncodingException;
34
import java.text.SimpleDateFormat;
35
import java.util.ArrayList;
36
import java.util.Collection;
37
import java.util.Collections;
38
import java.util.Date;
39
import java.util.Enumeration;
40
import java.util.Hashtable;
41
import java.util.Iterator;
42
import java.util.Locale;
43
import java.util.Map;
44
import java.util.TimeZone;
45
import java.util.Vector;
46

47
class ServletUnitHttpResponse implements HttpServletResponse {
1✔
48

49
    // rfc1123-date is "Sun, 06 Nov 1994 08:49:37 GMT"
50
    private static final String RFC1123_DATE_SPEC = "EEE, dd MMM yyyy HH:mm:ss z";
51
    private boolean _committed;
52
    private Locale _locale = Locale.getDefault();
1✔
53

54
    private static final Hashtable ENCODING_MAP = new Hashtable();
1✔
55

56
    /**
57
     * Adds the specified cookie to the response. It can be called multiple times to set more than one cookie.
58
     */
59
    @Override
60
    public void addCookie(Cookie cookie) {
61
        _cookies.addElement(cookie);
1✔
62
    }
1✔
63

64
    /**
65
     * Checks whether the response message header has a field with the specified name.
66
     */
67
    @Override
68
    public boolean containsHeader(String name) {
69
        return _headers.containsKey(name.toUpperCase());
1✔
70
    }
71

72
    /**
73
     * Encodes the specified URL by including the session ID in it, or, if encoding is not needed, returns the URL
74
     * unchanged. The implementation of this method should include the logic to determine whether the session ID needs
75
     * to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL
76
     * encoding is unnecessary.
77
     **/
78
    @Override
79
    public String encodeURL(String url) {
80
        return url;
×
81
    }
82

83
    /**
84
     * Encodes the specified URL for use in the <code>sendRedirect</code> method or, if encoding is not needed, returns
85
     * the URL unchanged. The implementation of this method should include the logic to determine whether the session ID
86
     * needs to be encoded in the URL. Because the rules for making this determination differ from those used to decide
87
     * whether to encode a normal link, this method is seperate from the <code>encodeUrl</code> method.
88
     **/
89
    @Override
90
    public String encodeRedirectURL(String url) {
91
        return url;
×
92
    }
93

94
    /**
95
     * Sends a temporary redirect response to the client using the specified redirect location URL. The URL must be
96
     * absolute (for example, <code><em>https://hostname/path/file.html</em></code>). Relative URLs are not permitted
97
     * here.
98
     */
99
    @Override
100
    public void sendRedirect(String location) throws IOException {
101
        setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
1✔
102
        setHeader("Location", location);
1✔
103
    }
1✔
104

105
    /**
106
     * Sends an error response to the client using the specified status code and descriptive message. If setStatus has
107
     * previously been called, it is reset to the error status code. The message is sent as the body of an HTML page,
108
     * which is returned to the user to describe the problem. The page is sent with a default HTML header; the message
109
     * is enclosed in simple body tags (&lt;body&gt;&lt;/body&gt;).
110
     **/
111
    @Override
112
    public void sendError(int sc) throws IOException {
113
        sendError(sc, "");
×
114
    }
×
115

116
    /**
117
     * Sends an error response to the client using the specified status code and descriptive message. If setStatus has
118
     * previously been called, it is reset to the error status code. The message is sent as the body of an HTML page,
119
     * which is returned to the user to describe the problem. The page is sent with a default HTML header; the message
120
     * is enclosed in simple body tags (&lt;body&gt;&lt;/body&gt;).
121
     **/
122
    @Override
123
    public void sendError(int sc, String msg) throws IOException {
124
        setStatus(sc);
×
125
        _statusMessage = msg;
×
126

127
        _writer = null;
×
128
        _servletStream = null;
×
129

130
        setContentType("text/html");
×
131
        getWriter().println("<html><head><title>" + msg + "</title></head><body>" + msg + "</body></html>");
×
132
    }
×
133

134
    /**
135
     * Sets the status code for this response. This method is used to set the return status code when there is no error
136
     * (for example, for the status codes SC_OK or SC_MOVED_TEMPORARILY). If there is an error, the
137
     * <code>sendError</code> method should be used instead.
138
     **/
139
    @Override
140
    public void setStatus(int sc) {
141
        _status = sc;
1✔
142
    }
1✔
143

144
    /**
145
     * Adds a field to the response header with the given name and value. If the field had already been set, the new
146
     * value overwrites the previous one. The <code>containsHeader</code> method can be used to test for the presence of
147
     * a header before setting its value.
148
     **/
149
    @Override
150
    public void setHeader(String name, String value) {
151
        ArrayList values = new ArrayList();
1✔
152
        values.add(value);
1✔
153
        synchronized (_headers) {
1✔
154
            _headers.put(name.toUpperCase(), values);
1✔
155
        }
1✔
156
    }
1✔
157

158
    /**
159
     * Adds a field to the response header with the given name and integer value. If the field had already been set, the
160
     * new value overwrites the previous one. The <code>containsHeader</code> method can be used to test for the
161
     * presence of a header before setting its value.
162
     **/
163
    @Override
164
    public void setIntHeader(String name, int value) {
165
        setHeader(name, asHeaderValue(value));
1✔
166
    }
1✔
167

168
    /**
169
     * Adds a field to the response header with the given name and integer value. If the field had already been set, the
170
     * new value overwrites the previous one. The <code>containsHeader</code> method can be used to test for the
171
     * presence of a header before setting its value.
172
     **/
173
    public void setLongHeader(String name, long value) {
174
        setHeader(name, asHeaderLongValue(value));
×
175
    }
×
176

177
    private String asHeaderValue(int value) {
178
        return Integer.toString(value);
1✔
179
    }
180

181
    private String asHeaderLongValue(long value) {
182
        return Long.toString(value);
×
183
    }
184

185
    /**
186
     * Adds a field to the response header with the given name and date-valued field. The date is specified in terms of
187
     * milliseconds since the epoch. If the date field had already been set, the new value overwrites the previous one.
188
     * The <code>containsHeader</code> method can be used to test for the presence of a header before setting its value.
189
     **/
190
    @Override
191
    public void setDateHeader(String name, long date) {
192
        setHeader(name, asDateHeaderValue(date));
1✔
193
    }
1✔
194

195
    private String asDateHeaderValue(long date) {
196
        Date value = new Date(date);
1✔
197
        SimpleDateFormat formatter = new SimpleDateFormat(RFC1123_DATE_SPEC, Locale.US);
1✔
198
        formatter.setTimeZone(TimeZone.getTimeZone("Greenwich Mean Time"));
1✔
199
        return formatter.format(value);
1✔
200
    }
201

202
    /**
203
     * Returns the name of the character set encoding used for the MIME body sent by this response.
204
     **/
205
    @Override
206
    public String getCharacterEncoding() {
207
        return _encoding == null ? HttpUnitUtils.DEFAULT_CHARACTER_SET : _encoding;
1✔
208
    }
209

210
    /**
211
     * Sets the content type of the response the server sends to the client. The content type may include the type of
212
     * character encoding used, for example, <code>text/html; charset=ISO-8859-4</code>.
213
     * <p>
214
     * You can only use this method once, and you should call it before you obtain a <code>PrintWriter</code> or
215
     * {@link ServletOutputStream} object to return a response.
216
     **/
217
    @Override
218
    public void setContentType(String type) {
219
        String[] typeAndEncoding = HttpUnitUtils.parseContentTypeHeader(type);
1✔
220

221
        _contentType = typeAndEncoding[0];
1✔
222
        if (typeAndEncoding[1] != null) {
1✔
223
            _encoding = typeAndEncoding[1];
1✔
224
        }
225
    }
1✔
226

227
    /**
228
     * Returns a {@link ServletOutputStream} suitable for writing binary data in the response. The servlet engine does
229
     * not encode the binary data.
230
     *
231
     * @exception IllegalStateException
232
     *                if you have already called the <code>getWriter</code> method
233
     **/
234
    @Override
235
    public ServletOutputStream getOutputStream() throws IOException {
236
        if (_writer != null) {
1✔
237
            throw new IllegalStateException("Tried to create output stream; writer already exists");
1✔
238
        }
239
        if (_servletStream == null) {
1✔
240
            _outputStream = new ByteArrayOutputStream();
1✔
241
            _servletStream = new ServletUnitOutputStream(_outputStream);
1✔
242
        }
243
        return _servletStream;
1✔
244
    }
245

246
    /**
247
     * Returns a <code>PrintWriter</code> object that you can use to send character text to the client. The character
248
     * encoding used is the one specified in the <code>charset=</code> property of the {@link #setContentType} method,
249
     * which you must call <i>before</i> you call this method.
250
     * <p>
251
     * If necessary, the MIME type of the response is modified to reflect the character encoding used.
252
     * <p>
253
     * You cannot use this method if you have already called {@link #getOutputStream} for this
254
     * <code>ServletResponse</code> object.
255
     *
256
     * @exception UnsupportedEncodingException
257
     *                if the character encoding specified in <code>setContentType</code> cannot be used
258
     * @exception IllegalStateException
259
     *                if the <code>getOutputStream</code> method has already been called for this response object; in
260
     *                that case, you can't use this method
261
     **/
262
    @Override
263
    public PrintWriter getWriter() throws UnsupportedEncodingException {
264
        if (_servletStream != null) {
1✔
265
            throw new IllegalStateException("Tried to create writer; output stream already exists");
1✔
266
        }
267
        if (_writer == null) {
1✔
268
            _outputStream = new ByteArrayOutputStream();
1✔
269
            _writer = new PrintWriter(new OutputStreamWriter(_outputStream, getCharacterEncoding()));
1✔
270
        }
271
        return _writer;
1✔
272
    }
273

274
    /**
275
     * Sets the length of the content the server returns to the client. In HTTP servlets, this method sets the HTTP
276
     * Content-Length header.
277
     **/
278
    @Override
279
    public void setContentLength(int len) {
280
        setIntHeader("Content-Length", len);
1✔
281
    }
1✔
282

283
    // ------------------------------- the following methods are new in JSDK 2.2 ----------------------
284

285
    /**
286
     * Adds a response header with the given name and value. This method allows response headers to have multiple
287
     * values.
288
     **/
289
    @Override
290
    public void addHeader(String name, String value) {
291
        synchronized (_headers) {
1✔
292
            String key = name.toUpperCase();
1✔
293
            ArrayList values = (ArrayList) _headers.get(key);
1✔
294
            if (values == null) {
1✔
295
                values = new ArrayList();
1✔
296
                _headers.put(key, values);
1✔
297
            }
298
            values.add(value);
1✔
299
        }
1✔
300
    }
1✔
301

302
    /**
303
     * Adds a response header with the given name and value. This method allows response headers to have multiple
304
     * values.
305
     **/
306
    @Override
307
    public void addIntHeader(String name, int value) {
308
        addHeader(name, asHeaderValue(value));
1✔
309
    }
1✔
310

311
    /**
312
     * Adds a response header with the given name and value. This method allows response headers to have multiple
313
     * values.
314
     **/
315
    @Override
316
    public void addDateHeader(String name, long value) {
317
        addHeader(name, asDateHeaderValue(value));
1✔
318
    }
1✔
319

320
    /**
321
     * Sets the preferred buffer size for the body of the response. The servlet container will use a buffer at least as
322
     * large as the size requested. The actual buffer size used can be found using getBufferSize.
323
     **/
324
    @Override
325
    public void setBufferSize(int size) {
326
        if (getContents().length != 0) {
1✔
327
            throw new IllegalStateException("May not set buffer size after data is written");
1✔
328
        }
329
    }
1✔
330

331
    /**
332
     * Returns the actual buffer size used for the response. If no buffering is used, this method returns 0.
333
     **/
334
    @Override
335
    public int getBufferSize() {
336
        return 0;
×
337
    }
338

339
    /**
340
     * Returns a boolean indicating if the response has been committed. A committed response has already had its status
341
     * code and headers written.
342
     **/
343
    @Override
344
    public boolean isCommitted() {
345
        return _committed;
1✔
346
    }
347

348
    /**
349
     * Forces any content in the buffer to be written to the client. A call to this method automatically commits the
350
     * response, meaning the status code and headers will be written.
351
     **/
352
    @Override
353
    public void flushBuffer() throws IOException {
354
        _committed = true;
1✔
355
    }
1✔
356

357
    /**
358
     * Clears any data that exists in the buffer as well as the status code and headers. If the response has been
359
     * committed, this method throws an IllegalStateException.
360
     **/
361
    @Override
362
    public void reset() {
363
        resetBuffer();
1✔
364
        _headers.clear();
1✔
365
        _headersComplete = false;
1✔
366
        _status = SC_OK;
1✔
367
    }
1✔
368

369
    /**
370
     * Sets the locale of the response, setting the headers (including the Content-Type's charset) as appropriate. This
371
     * method should be called before a call to getWriter(). By default, the response locale is the default locale for
372
     * the server.
373
     **/
374
    @Override
375
    public void setLocale(Locale locale) {
376
        _locale = locale;
1✔
377
        if (_encoding == null) {
1!
378
            for (Iterator it = ENCODING_MAP.entrySet().iterator(); it.hasNext();) {
1!
379
                Map.Entry entry = (Map.Entry) it.next();
1✔
380
                String locales = (String) entry.getValue();
1✔
381
                if (locales.indexOf(locale.getLanguage()) >= 0 || locales.indexOf(locale.toString()) >= 0) {
1!
382
                    _encoding = (String) entry.getKey();
1✔
383
                    return;
1✔
384
                }
385
            }
1✔
386
        }
387
    }
×
388

389
    /**
390
     * Returns the locale assigned to the response.
391
     **/
392
    @Override
393
    public Locale getLocale() {
394
        return _locale;
1✔
395
    }
396

397
    // ----------------------------- methods added to ServletResponse in JSDK 2.3 --------------------------------------
398

399
    /**
400
     * Clears the content of the underlying buffer in the response without clearing headers or status code. If the
401
     * response has been committed, this method throws an IllegalStateException.
402
     *
403
     * @since 1.3
404
     */
405
    @Override
406
    public void resetBuffer() {
407
        if (_committed) {
1✔
408
            throw new IllegalStateException("May not resetBuffer after response is committed");
1✔
409
        }
410
        _outputStream = null;
1✔
411
        _servletStream = null;
1✔
412
        _writer = null;
1✔
413
    }
1✔
414

415
    // ---------------------------------------------- package methods --------------------------------------------------
416

417
    /**
418
     * Returns the contents of this response.
419
     **/
420
    byte[] getContents() {
421
        if (_outputStream == null) {
1✔
422
            return new byte[0];
1✔
423
        }
424
        if (_writer != null) {
1✔
425
            _writer.flush();
1✔
426
        }
427
        return _outputStream.toByteArray();
1✔
428
    }
429

430
    /**
431
     * Returns the status of this response.
432
     **/
433
    @Override
434
    public int getStatus() {
435
        return _status;
1✔
436
    }
437

438
    /**
439
     * Returns the message associated with this response's status.
440
     **/
441
    String getMessage() {
442
        return _statusMessage;
×
443
    }
444

445
    public String[] getHeaderFieldNames() {
446
        if (!_headersComplete) {
×
447
            completeHeaders();
×
448
        }
449
        Vector names = new Vector();
×
450
        for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
×
451
            names.addElement(e.nextElement());
×
452
        }
453
        String[] result = new String[names.size()];
×
454
        names.copyInto(result);
×
455
        return result;
×
456
    }
457

458
    /**
459
     * Returns the headers defined for this response.
460
     *
461
     * @param name
462
     *            - the name of the field to get
463
     **/
464
    String getHeaderFieldDirect(String name) {
465
        ArrayList values;
466
        synchronized (_headers) {
1✔
467
            values = (ArrayList) _headers.get(name.toUpperCase());
1✔
468
        }
1✔
469

470
        return values == null ? null : (String) values.get(0);
1✔
471
    }
472

473
    /**
474
     * Returns the headers defined for this response.
475
     *
476
     * @param name
477
     **/
478
    String getHeaderField(String name) {
479
        if (!_headersComplete) {
1✔
480
            completeHeaders();
1✔
481
        }
482
        return getHeaderFieldDirect(name);
1✔
483
    }
484

485
    /**
486
     * Return an array of all the header values associated with the specified header name, or an zero-length array if
487
     * there are no such header values.
488
     *
489
     * @param name
490
     *            Header name to look up
491
     */
492
    public String[] getHeaderFields(String name) {
493
        if (!_headersComplete) {
1✔
494
            completeHeaders();
1✔
495
        }
496
        ArrayList values;
497
        synchronized (_headers) {
1✔
498
            values = (ArrayList) _headers.get(name.toUpperCase());
1✔
499
        }
1✔
500
        if (values == null) {
1✔
501
            return new String[0];
1✔
502
        }
503
        String results[] = new String[values.size()];
1✔
504
        return (String[]) values.toArray(results);
1✔
505

506
    }
507

508
    // --------------------------------------- methods added to ServletRequest in Servlet API 2.4
509
    // ----------------------------
510

511
    @Override
512
    public void setCharacterEncoding(String string) {
513
        _encoding = string;
×
514
    }
×
515

516
    /**
517
     * Returns the content type defined for this response.
518
     **/
519
    @Override
520
    public String getContentType() {
521
        return _contentType;
×
522
    }
523

524
    // ------------------------------------------- private members ------------------------------------
525

526
    private String _contentType = "text/plain";
1✔
527

528
    private String _encoding;
529

530
    private PrintWriter _writer;
531

532
    private ServletOutputStream _servletStream;
533

534
    private ByteArrayOutputStream _outputStream;
535

536
    private int _status = SC_OK;
1✔
537

538
    private String _statusMessage = "OK";
1✔
539

540
    private final Hashtable _headers = new Hashtable();
1✔
541

542
    private boolean _headersComplete;
543

544
    private Vector _cookies = new Vector();
1✔
545

546
    private void completeHeaders() {
547
        if (_headersComplete) {
1!
548
            return;
×
549
        }
550
        addCookieHeader();
1✔
551
        // BR 3301056 ServletUnit handling Content-Type incorrectly
552
        if (getHeaderFieldDirect("Content-Type") == null) {
1✔
553
            setHeader("Content-Type", _contentType + "; charset=" + getCharacterEncoding());
1✔
554
        }
555
        _headersComplete = true;
1✔
556
    }
1✔
557

558
    private void addCookieHeader() {
559
        if (_cookies.isEmpty()) {
1✔
560
            return;
1✔
561
        }
562

563
        StringBuilder sb = new StringBuilder();
1✔
564
        for (Enumeration e = _cookies.elements(); e.hasMoreElements();) {
1✔
565
            Cookie cookie = (Cookie) e.nextElement();
1✔
566
            sb.append(cookie.getName()).append('=').append(cookie.getValue());
1✔
567
            if (cookie.getPath() != null) {
1!
568
                sb.append(";path=").append(cookie.getPath());
1✔
569
            }
570
            if (cookie.getDomain() != null) {
1!
571
                sb.append(";domain=").append(cookie.getDomain());
×
572
            }
573
            if (e.hasMoreElements()) {
1!
574
                sb.append(',');
×
575
            }
576
        }
1✔
577
        setHeader("Set-Cookie", sb.toString());
1✔
578
    }
1✔
579

580
    static {
581
        ENCODING_MAP.put("iso-8859-1", "ca da de en es fi fr is it nl no pt sv ");
1✔
582
        ENCODING_MAP.put("iso-8859-2", "cs hr hu pl ro sh sk sl sq ");
1✔
583
        ENCODING_MAP.put("iso-8859-4", "et lt lv ");
1✔
584
        ENCODING_MAP.put("iso-8859-5", "be bg mk ru sr uk ");
1✔
585
        ENCODING_MAP.put("iso-8859-6", "ar ");
1✔
586
        ENCODING_MAP.put("iso-8859-7", "el ");
1✔
587
        ENCODING_MAP.put("iso-8859-8", "iw he ");
1✔
588
        ENCODING_MAP.put("iso-8859-9", "tr ");
1✔
589

590
        ENCODING_MAP.put("Shift_JIS", "ja ");
1✔
591
        ENCODING_MAP.put("EUC-KR", "ko ");
1✔
592
        ENCODING_MAP.put("TIS-620", "th ");
1✔
593
        ENCODING_MAP.put("GB2312", "zh ");
1✔
594
        ENCODING_MAP.put("Big5", "zh_TW zh_HK ");
1✔
595
    }
1✔
596

597
    @Override
598
    public String getHeader(String name) {
599
        return (String) _headers.get(name.toUpperCase());
×
600
    }
601

602
    @Override
603
    public Collection<String> getHeaders(String name) {
604
        ArrayList values;
605
        synchronized (_headers) {
×
606
            values = (ArrayList) _headers.get(name.toUpperCase());
×
607
        }
×
608
        if (values == null) {
×
609
            return Collections.EMPTY_LIST;
×
610
        }
611
        return values;
×
612
    }
613

614
    @Override
615
    public Collection<String> getHeaderNames() {
616
        if (!_headersComplete) {
×
617
            completeHeaders();
×
618
        }
619
        Vector names = new Vector();
×
620
        for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
×
621
            names.addElement(e.nextElement());
×
622
        }
623
        return names;
×
624
    }
625

626
    @Override
627
    public void setContentLengthLong(long len) {
628
        setLongHeader("Content-Length", len);
×
629
    }
×
630

631
    public void sendRedirect(String location, int sc, boolean clearBuffer) throws IOException {
NEW
632
        setStatus(sc);
×
NEW
633
        setHeader("Location", location);
×
NEW
634
        if (clearBuffer) {
×
NEW
635
            resetBuffer();
×
636
        }
NEW
637
    }
×
638

639
}
640

641
class ServletUnitOutputStream extends ServletOutputStream {
642

643
    ServletUnitOutputStream(ByteArrayOutputStream stream) {
1✔
644
        _stream = stream;
1✔
645
    }
1✔
646

647
    @Override
648
    public void write(int aByte) throws IOException {
649
        _stream.write(aByte);
1✔
650
    }
1✔
651

652
    private ByteArrayOutputStream _stream;
653

654
    @Override
655
    public boolean isReady() {
UNCOV
656
        return false;
×
657
    }
658

659
    @Override
660
    public void setWriteListener(WriteListener writeListener) {
661
        // Do nothing
662
    }
×
663
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc