AbstractDb.java

1
package com.renomad.minum.database;
2
3
import com.renomad.minum.logging.ILogger;
4
import com.renomad.minum.state.Context;
5
import com.renomad.minum.utils.IFileUtils;
6
7
import java.io.IOException;
8
import java.nio.file.Path;
9
import java.util.*;
10
import java.util.concurrent.Callable;
11
import java.util.concurrent.ConcurrentHashMap;
12
import java.util.concurrent.atomic.AtomicLong;
13
import java.util.concurrent.locks.ReentrantLock;
14
import java.util.function.Function;
15
16
/**
17
 * The abstract database class is a representation of the essential capabilities of
18
 * a Minum database.
19
 * <p>
20
 *     There are two kinds of database provided, which only differ in how they
21
 *     store data on disk.  The "classic" kind, {@link Db}, stores each piece of
22
 *     data in its own file.  This is the simplest approach.
23
 * </p>
24
 * <p>
25
 *     However, for significant speed gains, the new {@link DbEngine2} will
26
 *     store each change as an append to a file, and will consolidate the on-disk
27
 *     data occasionally, and on start.  That way is thousands of times faster
28
 *     to write to disk and to read from disk at startup.
29
 * </p>
30
 * @param <T> This is the type of data, which is always an implementation of
31
 *           the {@link DbData} class.  See the code of {@link com.renomad.minum.security.Inmate}
32
 *           for an example of how this should look.
33
 */
34
public abstract class AbstractDb<T extends DbData<?>> {
35
36
    /**
37
     * The directory of the database on disk
38
     */
39
    protected final Path dbDirectory;
40
41
    /**
42
     * An empty instance of the type of data stored by this
43
     * database, used for better handling of generics.
44
     */
45
    protected final T emptyInstance;
46
47
    /**
48
     * Used for handling some file utilities in the database like creating directories
49
     */
50
    protected final IFileUtils fileUtils;
51
52
    /**
53
     * Holds some system-wide information that is beneficial for components of the database
54
     */
55
    protected final Context context;
56
57
    /**
58
     * Used for providing logging throughout the database
59
     */
60
    protected final ILogger logger;
61
62
    /**
63
     * The internal data structure of the database that resides in memory.  The beating heart
64
     * of the database while it runs.
65
     */
66
    protected final Map<Long, T> data;
67
68
    /**
69
     * used to place locks around certain actions that need to avoid
70
     * thread interleaving.
71
     */
72
    protected final InspectableLock dbLock;
73
74
    /**
75
     * The current index, used when creating new data items.  Each item has its own
76
     * index value, this is where it is tracked.
77
     */
78
    protected AtomicLong index;
79
80
    // components for registered indexes (for faster read performance)
81
82
    /**
83
     * This data structure is a nested map used for providing indexed data search.
84
     * <br>
85
     * The outer map is between the name of the index and the inner map.
86
     * <br>
87
     * The inner map is between strings and sets of items related to that string.
88
     */
89
    protected final Map<String, Map<String, Set<T>>> registeredIndexes;
90
91
    /**
92
     * This map holds the functions that are registered to indexes, which are used
93
     * to construct the mappings between string values and items in the database.
94
     */
95
    protected final Map<String, Function<T, String>> partitioningMap;
96
97
    private final ReentrantLock indexLock;
98
99
    protected AbstractDb(Path dbDirectory, Context context, T instance, IFileUtils fileUtils) {
100 1 1. <init> : negated conditional → KILLED
        if (context.isDbPathRegistered(dbDirectory)) {
101
            throw new DbException("Attempted to register more than one database to the same path: " + dbDirectory);
102
        }
103 1 1. <init> : removed call to com/renomad/minum/state/Context::addToDbPaths → KILLED
        context.addToDbPaths(dbDirectory);
104
        this.dbDirectory = dbDirectory;
105
        this.context = context;
106
        this.emptyInstance = instance;
107
        this.data = new ConcurrentHashMap<>();
108
        this.logger = context.getLogger();
109
        this.registeredIndexes = new HashMap<>();
110
        this.partitioningMap = new HashMap<>();
111
        this.fileUtils = fileUtils;
112
        this.dbLock = new InspectableLock();
113
        this.indexLock = new ReentrantLock();
114
    }
115
116
    /**
117
     * Used to cleanly stop the database.
118
     * <br>
119
     * In the case of {@link Db} this will interrupt its internal queue and tell it
120
     * to finish up processing.
121
     * <br>
122
     * In the case of {@link DbEngine2} this will flush data to disk.
123
     */
124
    public abstract void stop() throws IOException;
125
126
    /**
127
     * Used to cleanly stop the database, with extra allowance of time
128
     * for cleanup.
129
     * <br>
130
     * Note that this method mostly applies to {@link Db}, and not as much
131
     * to {@link DbEngine2}.  Only Db uses a processing queue on a thread which
132
     * is what requires a longer shutdown time for interruption.
133
     * @param count number of loops before we are done waiting for a clean close
134
     *              and instead crash the instance closed.
135
     * @param sleepTime how long to wait, in milliseconds, for each iteration of the waiting loop.
136
     */
137
    public abstract void stop(int count, long sleepTime) throws IOException;
138
139
140
    /**
141
     * Write data to the database.  Use an index of 0 to store new data, and a positive
142
     * non-zero value to update data.
143
     * <p><em>
144
     * Example of adding new data to the database:
145
     * </em></p>
146
     * {@snippet :
147
     *          final var newSalt = StringUtils.generateSecureRandomString(10);
148
     *          final var hashedPassword = CryptoUtils.createPasswordHash(newPassword, newSalt);
149
     *          final var newUser = new User(0L, newUsername, hashedPassword, newSalt);
150
     *          userDb.write(newUser);
151
     * }
152
     * <p><em>
153
     * Example of updating data:
154
     * </em></p>
155
     * {@snippet :
156
     *         // write the updated salted password to the database
157
     *         final var updatedUser = new User(
158
     *                 user().getIndex(),
159
     *                 user().getUsername(),
160
     *                 hashedPassword,
161
     *                 newSalt);
162
     *         userDb.write(updatedUser);
163
     * }
164
     *
165
     * @param newData the data we are writing
166
     * @return the data with its new index assigned.
167
     */
168
    public abstract T write(T newData);
169
170
    /**
171
     * Write database data into memory
172
     * @param newData the new data may be totally new or an update
173
     * @param newElementCreated if true, this is a create.  If false, an update.
174
     */
175
    protected void writeToMemory(T newData, boolean newElementCreated) {
176
        // if we got here, we are safe to proceed with putting the data into memory and disk
177
        logger.logTrace(() -> String.format("in thread %d, writing data %s", Thread.currentThread().threadId(), newData));
178
        T oldData = data.put(newData.getIndex(), newData);
179
180
        // handle the indexes differently depending on whether this is a create or delete
181 1 1. writeToMemory : negated conditional → KILLED
        if (newElementCreated) {
182 1 1. writeToMemory : removed call to com/renomad/minum/database/AbstractDb::addToIndexes → KILLED
            addToIndexes(newData);
183
        } else {
184 1 1. writeToMemory : removed call to com/renomad/minum/database/AbstractDb::removeFromIndexes → KILLED
            removeFromIndexes(oldData);
185 1 1. writeToMemory : removed call to com/renomad/minum/database/AbstractDb::addToIndexes → KILLED
            addToIndexes(newData);
186
        }
187
    }
188
189
    /**
190
     * When new data comes in, we look at its "index" value. If
191
     * it is zero, it's a create, and we assign it a new value.  If it is
192
     * positive, it is an update, and we had better find it in the database
193
     * already, or else throw an exception.
194
     * @return true if a create, false if an update
195
     */
196
    protected boolean processDataIndex(T newData) {
197
        // *** deal with the in-memory portion ***
198
        boolean newElementCreated = false;
199
        // create a new index for the data, if needed
200 1 1. processDataIndex : negated conditional → KILLED
        if (newData.getIndex() == 0L) {
201 1 1. processDataIndex : removed call to com/renomad/minum/database/DbData::setIndex → KILLED
            newData.setIndex(index.getAndIncrement());
202
            newElementCreated = true;
203
        } else {
204
            // if the data does not exist, and a positive non-zero
205
            // index was provided, throw an exception.
206
            boolean dataEntryExists = data.containsKey(newData.getIndex());
207 1 1. processDataIndex : negated conditional → KILLED
            if (!dataEntryExists) {
208
                throw new DbException(
209
                        String.format("Positive indexes are only allowed when updating existing data. Index: %d",
210
                                newData.getIndex()));
211
            }
212
        }
213 2 1. processDataIndex : replaced boolean return with false for com/renomad/minum/database/AbstractDb::processDataIndex → KILLED
2. processDataIndex : replaced boolean return with true for com/renomad/minum/database/AbstractDb::processDataIndex → KILLED
        return newElementCreated;
214
    }
215
216
    /**
217
     * Delete data
218
     * <p><em>Example:</em></p>
219
     * {@snippet :
220
     *      userDb.delete(user);
221
     * }
222
     *
223
     * @param dataToDelete the data we are serializing and writing
224
     */
225
    public abstract void delete(T dataToDelete);
226
227
228
    /**
229
     * Remove a particular item from the internal data structure in memory
230
     */
231
    protected void deleteFromMemory(T dataToDelete) {
232
        long dataIndex;
233
        dataIndex = dataToDelete.getIndex();
234 1 1. deleteFromMemory : negated conditional → KILLED
        if (!data.containsKey(dataIndex)) {
235
            throw new DbException("no data was found with index of " + dataIndex);
236
        }
237
        long finalDataIndex = dataIndex;
238
        logger.logTrace(() -> String.format("in thread %d, deleting data with index %d", Thread.currentThread().threadId(), finalDataIndex));
239
        data.remove(dataIndex);
240 1 1. deleteFromMemory : removed call to com/renomad/minum/database/AbstractDb::removeFromIndexes → KILLED
        removeFromIndexes(dataToDelete);
241
242
        // if all the data was just now deleted, we need to
243
        // reset the index back to 1
244 1 1. deleteFromMemory : negated conditional → KILLED
        if (data.isEmpty()) {
245 1 1. deleteFromMemory : removed call to java/util/concurrent/atomic/AtomicLong::set → KILLED
            index.set(1L);
246
        }
247
    }
248
249
250
    /**
251
     *  add the data to registered indexes.
252
     *  <br>
253
     *  For each of the registered indexes,
254
     *  get the stored function to obtain a string value which helps divide
255
     *  the overall data into partitions.
256
     */
257
    protected void addToIndexes(T dbData) {
258
259
        for (var entry : partitioningMap.entrySet()) {
260
            // a function provided by the user to obtain an index-key: a unique or semi-unique
261
            // value to help partition / index the data
262
            Function<T, String> indexStringFunction = entry.getValue();
263
            String propertyAsString = indexStringFunction.apply(dbData);
264
            Map<String, Set<T>> stringIndexMap = registeredIndexes.get(entry.getKey());
265 1 1. addToIndexes : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
            indexLock.lock();
266
            try {
267 1 1. lambda$addToIndexes$2 : replaced return value with Collections.emptySet for com/renomad/minum/database/AbstractDb::lambda$addToIndexes$2 → KILLED
                stringIndexMap.computeIfAbsent(propertyAsString, k -> new HashSet<>());
268
                // if the index-key provides a 1-to-1 mapping to items, like UUIDs, then
269
                // each value will have only one item in the collection.  In other cases,
270
                // like when partitioning the data into multiple groups, there could easily
271
                // be many items per index value.
272
                Set<T> dataSet = stringIndexMap.get(propertyAsString);
273
                dataSet.add(dbData);
274
            } finally {
275 1 1. addToIndexes : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED
                indexLock.unlock();
276
            }
277
        }
278
    }
279
280
    /**
281
     * Run when an item is deleted from the database
282
     */
283
    private void removeFromIndexes(T dbData) {
284
        for (var entry : partitioningMap.entrySet()) {
285
            // a function provided by the user to obtain an index-key: a unique or semi-unique
286
            // value to help partition / index the data
287
            Function<T, String> indexStringFunction = entry.getValue();
288
            String propertyAsString = indexStringFunction.apply(dbData);
289
            Map<String, Set<T>> stringIndexMap = registeredIndexes.get(entry.getKey());
290 1 1. removeFromIndexes : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
            indexLock.lock();
291
            try {
292 2 1. lambda$removeFromIndexes$3 : replaced boolean return with true for com/renomad/minum/database/AbstractDb::lambda$removeFromIndexes$3 → KILLED
2. lambda$removeFromIndexes$3 : negated conditional → KILLED
                stringIndexMap.get(propertyAsString).removeIf(x -> x.getIndex() == dbData.getIndex());
293
294
                // in certain cases, we're removing one of the items that is indexed but
295
                // there are more left.  If there's nothing left though, we'll remove the mapping.
296 1 1. removeFromIndexes : negated conditional → KILLED
                if (stringIndexMap.get(propertyAsString).isEmpty()) {
297
                    stringIndexMap.remove(propertyAsString);
298
                }
299
            } finally {
300 1 1. removeFromIndexes : removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED
                indexLock.unlock();
301
            }
302
        }
303
    }
304
305
306
    /**
307
     * Cause the database to immediately load all its data.
308
     * <p>
309
     *     If this method is not invoked by the developer, then the data will be loaded
310
     *     lazily, at the first point where it is needed, such as when
311
     *     getting or writing data.
312
     * </p>
313
     * <p>
314
     *     It is a good idea to regularly use this method at database
315
     *     initialization.  By doing so, the check for data corruption issues will
316
     *     throw an exception that will cause the whole application to halt
317
     *     immediately, which is far preferable to having the exception show
318
     *     as a complaint in the logs.
319
     * </p>
320
     * <p>
321
     *     An example database initialization with bells and whistles
322
     *     is as follows:
323
     * </p>
324
     * <pre>
325
     *     {@code
326
     *     AbstractDb<PersonName> sampleDomainDb = context.getDb2("names", PersonName.EMPTY)
327
     *          .registerIndex("name_index", name -> name)
328
     *          .loadData();
329
     *     }
330
     * </pre>
331
     */
332
    public abstract AbstractDb<T> loadData();
333
334
    /**
335
     * This method returns a read-only view of the values of a database.
336
     *
337
     * <p><em>Example:</em></p>
338
     * {@snippet :
339
     * boolean doesUserAlreadyExist(String username) {
340
     *     return userDb.values().stream().anyMatch(x -> x.getUsername().equals(username));
341
     * }
342
     * }
343
     */
344
    public abstract Collection<T> values();
345
346
    /**
347
     * Register an index in the database for higher performance data access.
348
     * <p>
349
     *     This command should be run immediately after database declaration,
350
     *     or more specifically, before any data is loaded from disk. Otherwise,
351
     *     it would be possible to skip indexing that data.
352
     * </p>
353
     * <br>
354
     * Example:
355
     * <pre>
356
     *     {@code
357
     *      final var myDatabase = context.getDb("photos", Photograph.EMPTY)
358
     *               .registerIndex("url", photo -> photo.getUrl())
359
     *               .loadData();
360
     *     }
361
     * </pre>
362
     * @param indexName a string used to distinguish this index.  This string will be used again
363
     *                  when requesting data in a method like {@link #getIndexedData} or {@link #findExactlyOne}
364
     * @param keyObtainingFunction a function which obtains data from the data in this database, used
365
     *                             to partition the data into groups (potentially up to a 1-to-1 correspondence
366
     *                             between id and object)
367
     * @return the database instance if the registration succeeded
368
     * @throws DbException if the parameters are not entered properly, if the index has already
369
     * been registered, or if the data has already been loaded. It is necessary that
370
     * this is run immediately after declaring the database. To explain further: the data is not
371
     * actually loaded until the first time it is needed, such as running a write or delete, or
372
     * if the {@link #loadData()} ()} method is run.  Creating an index map for the data that
373
     * is read from disk only occurs once, at data load time.  Thus, it is crucial that the
374
     * registerIndex command is run before any data is loaded.
375
     */
376
    public AbstractDb<T> registerIndex(String indexName, Function<T, String> keyObtainingFunction) {
377 1 1. registerIndex : negated conditional → KILLED
        if (keyObtainingFunction == null) {
378
            throw new DbException("When registering an index, the partitioning algorithm must not be null");
379
        }
380 2 1. registerIndex : negated conditional → KILLED
2. registerIndex : negated conditional → KILLED
        if (indexName == null || indexName.isBlank()) {
381
            throw new DbException("When registering an index, value must be a non-empty string");
382
        }
383 1 1. registerIndex : negated conditional → KILLED
        if (registeredIndexes.containsKey(indexName)) {
384
            throw new DbException("It is forbidden to register the same index more than once.  Duplicate index: \""+indexName+"\"");
385
        }
386
        HashMap<String, Set<T>> stringCollectionHashMap = new HashMap<>();
387
        registeredIndexes.put(indexName, stringCollectionHashMap);
388
        partitioningMap.put(indexName, keyObtainingFunction);
389 1 1. registerIndex : replaced return value with null for com/renomad/minum/database/AbstractDb::registerIndex → KILLED
        return this;
390
    }
391
392
    /**
393
     * Given the name of a registered index (see {@link #registerIndex(String, Function)}),
394
     * use the key to find the collection of data that matches it.
395
     * @param indexName the name of an index
396
     * @param key a string value that matches a partition calculated from the partition
397
     *            function provided to {@link #registerIndex(String, Function)}
398
     * @return a collection of data, an empty collection if nothing found
399
     */
400
    public Collection<T> getIndexedData(String indexName, String key) {
401 1 1. getIndexedData : negated conditional → KILLED
        if (!registeredIndexes.containsKey(indexName)) {
402
            throw new DbException("There is no index registered on the database Db<"+this.emptyInstance.getClass().getSimpleName()+"> with a name of \""+indexName+"\"");
403
        }
404 1 1. getIndexedData : removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED
        indexLock.lock();
405
        try {
406
            Set<T> ts = registeredIndexes.get(indexName).get(key);
407 1 1. getIndexedData : negated conditional → KILLED
            if (ts != null) {
408 1 1. getIndexedData : replaced return value with Collections.emptyList for com/renomad/minum/database/AbstractDb::getIndexedData → KILLED
                return new HashSet<>(ts);
409
            } else {
410
                return Set.of();
411
            }
412
        } finally {
413 1 1. getIndexedData : removed call to java/util/concurrent/locks/ReentrantLock::unlock → TIMED_OUT
            indexLock.unlock();
414
        }
415
    }
416
417
    /**
418
     * Get a set of the currently-registered indexes on this database, useful
419
     * for debugging.
420
     */
421
    public Set<String> getSetOfIndexes() {
422 1 1. getSetOfIndexes : replaced return value with Collections.emptySet for com/renomad/minum/database/AbstractDb::getSetOfIndexes → KILLED
        return partitioningMap.keySet();
423
    }
424
425
    /**
426
     * A utility to find exactly one item from the database.
427
     * <br>
428
     * This utility will search the indexes for a particular data by
429
     * indexName and indexKey.  If not found, it will return null. If
430
     * found, it will be returned. If more than one are found, an exception
431
     * will be thrown.  Use this tool when the data has been uniquely
432
     * indexed, like for example when setting a unique identifier into
433
     * each data.
434
     * @param indexName the name of the index, an arbitrary value set by the
435
     *                  user to help distinguish among potentially many indexes
436
     *                  set on this data
437
     * @param indexKey the key for this particular value, such as a UUID or a name
438
     *                 or any other way to partition the data
439
     * @see #findExactlyOne(String, String, Callable)
440
     */
441
    public T findExactlyOne(String indexName, String indexKey) {
442 1 1. findExactlyOne : replaced return value with null for com/renomad/minum/database/AbstractDb::findExactlyOne → KILLED
        return findExactlyOne(indexName, indexKey, () -> null);
443
    }
444
445
    /**
446
     * Find one item, with an alternate value if nothing was found
447
     * <br>
448
     * This utility will search the indexes for a particular data by
449
     * indexName and indexKey.  If not found, it will return an alternate
450
     * value provided by the "alternate" parameter. If
451
     * found, it will be returned. If more than one are found, an exception
452
     * will be thrown.  Use this tool when the data has been uniquely
453
     * indexed, like for example when setting a unique identifier into
454
     * each data.
455
     * @param indexName the name of the index, an arbitrary value set by the
456
     *                  user to help distinguish among potentially many indexes
457
     *                  set on this data
458
     * @param indexKey the key for this particular value, such as a UUID or a name
459
     *                 or any other way to partition the data
460
     * @param alternate a functional interface that will be run if no result
461
     *                  was found
462
     * @see #findExactlyOne(String, String)
463
     */
464
    public T findExactlyOne(String indexName, String indexKey, Callable<T> alternate) {
465
        Collection<T> indexedData = getIndexedData(indexName, indexKey);
466 1 1. findExactlyOne : negated conditional → KILLED
        if (indexedData.isEmpty()) {
467
            try {
468 1 1. findExactlyOne : replaced return value with null for com/renomad/minum/database/AbstractDb::findExactlyOne → KILLED
                return alternate.call();
469
            } catch (Exception ex) {
470
                throw new DbException(ex);
471
            }
472 1 1. findExactlyOne : negated conditional → KILLED
        } else if (indexedData.size() == 1) {
473 1 1. findExactlyOne : replaced return value with null for com/renomad/minum/database/AbstractDb::findExactlyOne → KILLED
            return indexedData.stream().findFirst().orElseThrow();
474
        } else {
475
            throw new DbException("More than one item found when searching database Db<%s> on index \"%s\" with key %s"
476
                    .formatted(emptyInstance.getClass().getSimpleName(), indexName, indexKey));
477
        }
478
    }
479
480
    /**
481
     * Provides access to the lock that is used around all
482
     * modifications to the database.  Useful for wrapping
483
     * around multiple statements when you need to ensure
484
     * nothing else can intervene while they run.
485
     * <br>
486
     * Here is an example of usage:
487
     * <pre>
488
     * {@code
489
     *     ReentrantLock dbLock = db.getDbLock();
490
     *     dbLock.lock();
491
     *     try {
492
     *         db.write(foo);
493
     *         db.write(bar);
494
     *     } finally {
495
     *         dbLock.unlock();
496
     *     }
497
     * }
498
     * </pre>
499
     */
500
    public InspectableLock getDbLock() {
501 1 1. getDbLock : replaced return value with null for com/renomad/minum/database/AbstractDb::getDbLock → KILLED
        return dbLock;
502
    }
503
504
    /**
505
     * Check that the input is non-null and has a positive index
506
     */
507
    protected static <T extends DbData<?>> void basicDataChecks(T newData) {
508
        // disallowed to be null input
509 1 1. basicDataChecks : negated conditional → KILLED
        if (newData == null) {
510
            throw new DbException("Incoming data was null");
511
        }
512
        // disallowed for input to have a negative index
513 2 1. basicDataChecks : negated conditional → KILLED
2. basicDataChecks : changed conditional boundary → KILLED
        if (newData.getIndex() < 0L) throw new DbException("Negative indexes are disallowed");
514
    }
515
516
}

Mutations

100

1.1
Location : <init>
Killed by : com.renomad.minum.utils.ActionQueueKillerTests
negated conditional → KILLED

103

1.1
Location : <init>
Killed by : com.renomad.minum.FunctionalTests.test_EdgeCase_BadRequest2(com.renomad.minum.FunctionalTests)
removed call to com/renomad/minum/state/Context::addToDbPaths → KILLED

181

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

182

1.1
Location : writeToMemory
Killed by : com.renomad.minum.web.WebTests
removed call to com/renomad/minum/database/AbstractDb::addToIndexes → KILLED

184

1.1
Location : writeToMemory
Killed by : com.renomad.minum.security.TheBrigTests
removed call to com/renomad/minum/database/AbstractDb::removeFromIndexes → KILLED

185

1.1
Location : writeToMemory
Killed by : com.renomad.minum.security.TheBrigTests
removed call to com/renomad/minum/database/AbstractDb::addToIndexes → KILLED

200

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

201

1.1
Location : processDataIndex
Killed by : com.renomad.minum.security.TheBrigTests
removed call to com/renomad/minum/database/DbData::setIndex → KILLED

207

1.1
Location : processDataIndex
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

213

1.1
Location : processDataIndex
Killed by : com.renomad.minum.web.WebTests
replaced boolean return with false for com/renomad/minum/database/AbstractDb::processDataIndex → KILLED

2.2
Location : processDataIndex
Killed by : com.renomad.minum.web.WebTests
replaced boolean return with true for com/renomad/minum/database/AbstractDb::processDataIndex → KILLED

234

1.1
Location : deleteFromMemory
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

240

1.1
Location : deleteFromMemory
Killed by : com.renomad.minum.security.TheBrigTests
removed call to com/renomad/minum/database/AbstractDb::removeFromIndexes → KILLED

244

1.1
Location : deleteFromMemory
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

245

1.1
Location : deleteFromMemory
Killed by : com.renomad.minum.database.DbEngine2Tests
removed call to java/util/concurrent/atomic/AtomicLong::set → KILLED

265

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

267

1.1
Location : lambda$addToIndexes$2
Killed by : com.renomad.minum.web.WebTests
replaced return value with Collections.emptySet for com/renomad/minum/database/AbstractDb::lambda$addToIndexes$2 → KILLED

275

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

290

1.1
Location : removeFromIndexes
Killed by : com.renomad.minum.security.TheBrigTests
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

292

1.1
Location : lambda$removeFromIndexes$3
Killed by : com.renomad.minum.security.TheBrigTests
replaced boolean return with true for com/renomad/minum/database/AbstractDb::lambda$removeFromIndexes$3 → KILLED

2.2
Location : lambda$removeFromIndexes$3
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

296

1.1
Location : removeFromIndexes
Killed by : com.renomad.minum.security.TheBrigTests
negated conditional → KILLED

300

1.1
Location : removeFromIndexes
Killed by : com.renomad.minum.security.TheBrigTests
removed call to java/util/concurrent/locks/ReentrantLock::unlock → KILLED

377

1.1
Location : registerIndex
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

380

1.1
Location : registerIndex
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

2.2
Location : registerIndex
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

383

1.1
Location : registerIndex
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

389

1.1
Location : registerIndex
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
replaced return value with null for com/renomad/minum/database/AbstractDb::registerIndex → KILLED

401

1.1
Location : getIndexedData
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

404

1.1
Location : getIndexedData
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
removed call to java/util/concurrent/locks/ReentrantLock::lock → KILLED

407

1.1
Location : getIndexedData
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

408

1.1
Location : getIndexedData
Killed by : com.renomad.minum.web.WebTests
replaced return value with Collections.emptyList for com/renomad/minum/database/AbstractDb::getIndexedData → KILLED

413

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

422

1.1
Location : getSetOfIndexes
Killed by : com.renomad.minum.database.DbEngine2Tests
replaced return value with Collections.emptySet for com/renomad/minum/database/AbstractDb::getSetOfIndexes → KILLED

442

1.1
Location : findExactlyOne
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
replaced return value with null for com/renomad/minum/database/AbstractDb::findExactlyOne → KILLED

466

1.1
Location : findExactlyOne
Killed by : com.renomad.minum.web.WebPerformanceTests.webPerfTest(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

468

1.1
Location : findExactlyOne
Killed by : com.renomad.minum.FunctionalTests.testEndToEnd_Functional(com.renomad.minum.FunctionalTests)
replaced return value with null for com/renomad/minum/database/AbstractDb::findExactlyOne → KILLED

472

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

473

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

501

1.1
Location : getDbLock
Killed by : com.renomad.minum.database.DbEngine2Tests
replaced return value with null for com/renomad/minum/database/AbstractDb::getDbLock → KILLED

509

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

513

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

2.2
Location : basicDataChecks
Killed by : com.renomad.minum.web.WebTests
changed conditional boundary → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0