TestLogger.java

1
package com.renomad.minum.logging;
2
3
import com.renomad.minum.state.Constants;
4
import com.renomad.minum.utils.MyThread;
5
6
import java.util.List;
7
import java.util.Locale;
8
import java.util.Queue;
9
import java.util.concurrent.ExecutorService;
10
import java.util.concurrent.atomic.AtomicInteger;
11
import java.util.concurrent.locks.ReentrantLock;
12
13
/**
14
 * This implementation of {@link Logger} has a few
15
 * extra functions that only apply to tests, like {@link #test(String)}
16
 */
17
public final class TestLogger extends Logger {
18
19
    private final Queue<String> recentLogLines;
20
    public static final int MAX_CACHE_SIZE = 30;
21
    private final ReentrantLock loggingLock;
22
    private final AtomicInteger testCount = new AtomicInteger(0);
23
24
    /**
25
     * See {@link TestLogger}
26
     */
27
    public TestLogger(Constants constants, ExecutorService executorService, String name) {
28
        super(constants, executorService, name);
29
        this.recentLogLines = new TestLoggerQueue(MAX_CACHE_SIZE);
30
        this.loggingLock = new ReentrantLock();
31
    }
32
33
    /**
34
     * A helper to get the string message value out of a
35
     * {@link ThrowingSupplier}
36
     */
37
    private String extractMessage(ThrowingSupplier<String, Exception> msg) {
38
        String receivedMessage;
39
        try {
40
            receivedMessage = msg.get();
41
        } catch (Exception ex) {
42
            receivedMessage = "EXCEPTION DURING GET: " + ex;
43
        }
44 1 1. extractMessage : replaced return value with "" for com/renomad/minum/logging/TestLogger::extractMessage → KILLED
        return receivedMessage;
45
    }
46
47
    /**
48
     * Keeps a record of the recently-added log messages, which is
49
     * useful for some tests.
50
     */
51
    private void addToCache(ThrowingSupplier<String, Exception> msg) {
52
        // put log messages into the tail of the queue
53
        String message = extractMessage(msg);
54 1 1. addToCache : negated conditional → TIMED_OUT
        String safeMessage = message == null ? "(null message)" : message;
55
        recentLogLines.add(safeMessage);
56
    }
57
58
    @Override
59
    public void logDebug(ThrowingSupplier<String, Exception> msg) {
60 1 1. logDebug : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loggingLock.lock();
61
        try {
62
            addToCache(msg);
63
            super.logDebug(msg);
64
        } finally {
65 1 1. logDebug : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED
            loggingLock.unlock();
66
        }
67
    }
68
69
    @Override
70
    public void logWarn(ThrowingSupplier<String, Exception> msg) {
71 1 1. logWarn : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loggingLock.lock();
72
        try {
73
            addToCache(msg);
74
            super.logWarn(msg);
75
        } finally {
76 1 1. logWarn : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED
            loggingLock.unlock();
77
        }
78
    }
79
80
    @Override
81
    public void logTrace(ThrowingSupplier<String, Exception> msg) {
82 1 1. logTrace : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loggingLock.lock();
83
        try {
84
            addToCache(msg);
85
            super.logTrace(msg);
86
        } finally {
87 1 1. logTrace : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            loggingLock.unlock();
88
        }
89
    }
90
91
    @Override
92
    public void logAudit(ThrowingSupplier<String, Exception> msg) {
93 1 1. logAudit : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loggingLock.lock();
94
        try {
95
            addToCache(msg);
96
            super.logAudit(msg);
97
        } finally {
98 1 1. logAudit : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            loggingLock.unlock();
99
        }
100
    }
101
102
    @Override
103
    public void logAsyncError(ThrowingSupplier<String, Exception> msg) {
104 1 1. logAsyncError : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loggingLock.lock();
105
        try {
106
            addToCache(msg);
107
            super.logAsyncError(msg);
108
        } finally {
109 1 1. logAsyncError : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            loggingLock.unlock();
110
        }
111
    }
112
113
    /**
114
     * Provides an ability to search over the recent past log messages,
115
     * case-insensitively.
116
     * @param lines number of lines of log messages to look back through,
117
     *              up to {@link #MAX_CACHE_SIZE}
118
     */
119
    public String findFirstMessageThatContains(String value, int lines) {
120
        List<String> values = findMessage(value, lines, recentLogLines);
121
        List<String> logsBeingSearched = logLinesToSearch(lines, recentLogLines);
122
        return checkValidityOfResults(value, values, logsBeingSearched);
123
    }
124
125
    /**
126
     * This is used in {@link #findFirstMessageThatContains(String, int)} to
127
     * handle exceptional situations with the results.  Specifically, exceptions
128
     * are:
129
     * <ol>
130
     *    <li>If there were no results found</li>
131
     *    <li>If there were multiple results found</li>
132
     * </ol>
133
     */
134
    static String checkValidityOfResults(String value, List<String> values, List<String> recentLogLines) {
135
        int size = values.size();
136 1 1. checkValidityOfResults : negated conditional → KILLED
        if (size == 0) {
137
            throw new TestLoggerException(value + " was not found in \n\t" + String.join("\n\t", recentLogLines));
138 2 1. checkValidityOfResults : changed conditional boundary → KILLED
2. checkValidityOfResults : negated conditional → KILLED
        } else if (size >= 2) {
139
            throw new TestLoggerException("multiple values of "+value+" found in: " + recentLogLines);
140
        } else {
141 1 1. checkValidityOfResults : replaced return value with "" for com/renomad/minum/logging/TestLogger::checkValidityOfResults → KILLED
            return values.getFirst();
142
        }
143
    }
144
145
    /**
146
     * Whether the given string exists in the log messages. May
147
     * exist multiple times.
148
     * @param value a string to search in the log
149
     * @param lines how many lines back to examine
150
     * @return whether this string was found, even if there
151
     *      were multiple places it was found.
152
     */
153
    public boolean doesMessageExist(String value, int lines) {
154
        if (! findMessage(value, lines, recentLogLines).isEmpty()) {
155 1 1. doesMessageExist : replaced boolean return with false for com/renomad/minum/logging/TestLogger::doesMessageExist → TIMED_OUT
            return true;
156
        } else {
157
            List<String> logsBeingSearched = logLinesToSearch(lines, recentLogLines);
158
            throw new TestLoggerException(value + " was not found in \n\t" + String.join("\n\t", logsBeingSearched));
159
        }
160
    }
161
162
    /**
163
     * Whether the given string exists in the log messages. May
164
     * exist multiple times.
165
     * @param value a string to search in the log
166
     * @return whether or not this string was found, even if there
167
     * were multiple places it was found.
168
     */
169
    public boolean doesMessageExist(String value) {
170
        return doesMessageExist(value, 3);
171
    }
172
173
    static List<String> findMessage(String value, int lines, Queue<String> recentLogLines) {
174 2 1. findMessage : changed conditional boundary → KILLED
2. findMessage : negated conditional → KILLED
        if (lines > MAX_CACHE_SIZE) {
175
            throw new TestLoggerException(String.format("Can only get up to %s lines from the log", MAX_CACHE_SIZE));
176
        }
177 2 1. findMessage : negated conditional → KILLED
2. findMessage : changed conditional boundary → KILLED
        if (lines <= 0) {
178
            throw new TestLoggerException("number of recent log lines must be a positive number");
179
        }
180
        MyThread.sleep(20);
181
        var lineList = logLinesToSearch(lines, recentLogLines);
182 3 1. lambda$findMessage$0 : replaced boolean return with true for com/renomad/minum/logging/TestLogger::lambda$findMessage$0 → TIMED_OUT
2. lambda$findMessage$0 : replaced boolean return with false for com/renomad/minum/logging/TestLogger::lambda$findMessage$0 → KILLED
3. findMessage : replaced return value with Collections.emptyList for com/renomad/minum/logging/TestLogger::findMessage → KILLED
        return lineList.stream().filter(x -> x.toLowerCase(Locale.ROOT).contains(value.toLowerCase(Locale.ROOT))).toList();
183
184
    }
185
186
    private static List<String> logLinesToSearch(int lines, Queue<String> recentLogLines) {
187 1 1. logLinesToSearch : Replaced integer subtraction with addition → TIMED_OUT
        var fromIndex = Math.max(recentLogLines.size() - lines, 0);
188 1 1. logLinesToSearch : replaced return value with Collections.emptyList for com/renomad/minum/logging/TestLogger::logLinesToSearch → TIMED_OUT
        return recentLogLines.stream().toList().subList(fromIndex, recentLogLines.size());
189
    }
190
191
    /**
192
     * Looks back through the last 3 log messages for one that
193
     * contains the provided value.  Returns the whole line if
194
     * found and an exception if not found.
195
     * <p>
196
     *     See {@link #findFirstMessageThatContains(String, int)} if you
197
     *     want to search through more than 3.  However, it is only
198
     *     possible to search up to {@link #MAX_CACHE_SIZE}
199
     * </p>
200
     */
201
    public String findFirstMessageThatContains(String value) {
202
        return findFirstMessageThatContains(value, 3);
203
    }
204
205
    /**
206
     * A helper function to log a test title prefixed with "TEST:"
207
     * <br>
208
     * Also collects data about the previously-run test
209
     */
210
    public void test(String msg) {
211
        // put together some pretty-looking text graphics to show the suiteName of our test in log
212 1 1. test : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        loggingLock.lock();
213
        try {
214
            final var baseLength = 11;
215 1 1. test : Replaced integer addition with subtraction → KILLED
            final var dashes = "-".repeat(msg.length() + baseLength);
216
217
            final int currentCount = testCount.incrementAndGet();
218 1 1. test : removed call to com/renomad/minum/queue/AbstractActionQueue::enqueue → TIMED_OUT
            loggingActionQueue.enqueue("Testlogger#test("+msg+")", () -> {
219
                System.out.printf("%n+%s+%n| TEST %d: %s |%n+%s+%n%n", dashes, currentCount, msg, dashes);
220
                recentLogLines.add(msg);
221
            });
222
        } finally {
223 1 1. test : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            loggingLock.unlock();
224
        }
225
    }
226
227
    public int getTestCount() {
228 1 1. getTestCount : replaced int return with 0 for com/renomad/minum/logging/TestLogger::getTestCount → KILLED
        return testCount.get();
229
    }
230
231
    @Override
232
    public String toString() {
233 1 1. toString : replaced return value with "" for com/renomad/minum/logging/TestLogger::toString → KILLED
        return "TestLogger using queue: " + super.loggingActionQueue.toString();
234
    }
235
236
}

Mutations

44

1.1
Location : extractMessage
Killed by : com.renomad.minum.utils.FileUtilsTests
replaced return value with "" for com/renomad/minum/logging/TestLogger::extractMessage → KILLED

54

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

60

1.1
Location : logDebug
Killed by : com.renomad.minum.web.PathDetailsTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

65

1.1
Location : logDebug
Killed by : com.renomad.minum.web.ServerTests
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

71

1.1
Location : logWarn
Killed by : com.renomad.minum.web.ServerTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

76

1.1
Location : logWarn
Killed by : com.renomad.minum.web.WebTests
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

82

1.1
Location : logTrace
Killed by : com.renomad.minum.web.SocketWrapperTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

87

1.1
Location : logTrace
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

93

1.1
Location : logAudit
Killed by : com.renomad.minum.FunctionalTests.testEndToEnd_Functional(com.renomad.minum.FunctionalTests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

98

1.1
Location : logAudit
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

104

1.1
Location : logAsyncError
Killed by : com.renomad.minum.utils.ThrowingRunnableTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

109

1.1
Location : logAsyncError
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

136

1.1
Location : checkValidityOfResults
Killed by : com.renomad.minum.utils.ThrowingRunnableTests
negated conditional → KILLED

138

1.1
Location : checkValidityOfResults
Killed by : com.renomad.minum.logging.TestLoggerTests
changed conditional boundary → KILLED

2.2
Location : checkValidityOfResults
Killed by : com.renomad.minum.utils.ThrowingRunnableTests
negated conditional → KILLED

141

1.1
Location : checkValidityOfResults
Killed by : com.renomad.minum.utils.ThrowingRunnableTests
replaced return value with "" for com/renomad/minum/logging/TestLogger::checkValidityOfResults → KILLED

155

1.1
Location : doesMessageExist
Killed by : none
replaced boolean return with false for com/renomad/minum/logging/TestLogger::doesMessageExist → TIMED_OUT

174

1.1
Location : findMessage
Killed by : com.renomad.minum.web.ServerTests
changed conditional boundary → KILLED

2.2
Location : findMessage
Killed by : com.renomad.minum.utils.FileUtilsTests
negated conditional → KILLED

177

1.1
Location : findMessage
Killed by : com.renomad.minum.utils.FileUtilsTests
negated conditional → KILLED

2.2
Location : findMessage
Killed by : com.renomad.minum.web.ServerTests
changed conditional boundary → KILLED

182

1.1
Location : lambda$findMessage$0
Killed by : com.renomad.minum.utils.FileUtilsTests
replaced boolean return with false for com/renomad/minum/logging/TestLogger::lambda$findMessage$0 → KILLED

2.2
Location : findMessage
Killed by : com.renomad.minum.utils.FileUtilsTests
replaced return value with Collections.emptyList for com/renomad/minum/logging/TestLogger::findMessage → KILLED

3.3
Location : lambda$findMessage$0
Killed by : none
replaced boolean return with true for com/renomad/minum/logging/TestLogger::lambda$findMessage$0 → TIMED_OUT

187

1.1
Location : logLinesToSearch
Killed by : none
Replaced integer subtraction with addition → TIMED_OUT

188

1.1
Location : logLinesToSearch
Killed by : none
replaced return value with Collections.emptyList for com/renomad/minum/logging/TestLogger::logLinesToSearch → TIMED_OUT

212

1.1
Location : test
Killed by : com.renomad.minum.web.PathDetailsTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

215

1.1
Location : test
Killed by : com.renomad.minum.utils.MyThreadTests
Replaced integer addition with subtraction → KILLED

218

1.1
Location : test
Killed by : none
removed call to com/renomad/minum/queue/AbstractActionQueue::enqueue → TIMED_OUT

223

1.1
Location : test
Killed by : none
removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT

228

1.1
Location : getTestCount
Killed by : com.renomad.minum.logging.TestLoggerTests
replaced int return with 0 for com/renomad/minum/logging/TestLogger::getTestCount → KILLED

233

1.1
Location : toString
Killed by : com.renomad.minum.web.RequestTests
replaced return value with "" for com/renomad/minum/logging/TestLogger::toString → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0