|
28 | 28 | import java.nio.file.spi.FileSystemProvider; |
29 | 29 | import java.time.Instant; |
30 | 30 | import java.util.HashMap; |
| 31 | +import java.util.Iterator; |
31 | 32 | import java.util.List; |
32 | 33 | import java.util.Map; |
| 34 | +import java.util.ServiceConfigurationError; |
33 | 35 | import java.util.ServiceLoader; |
34 | 36 | import java.util.Set; |
35 | 37 | import java.util.UUID; |
|
55 | 57 | import org.apache.cassandra.db.ColumnFamilyStore; |
56 | 58 | import org.apache.cassandra.db.Directories; |
57 | 59 | import org.apache.cassandra.db.Keyspace; |
| 60 | +import org.apache.cassandra.exceptions.ConfigurationException; |
58 | 61 | import org.apache.cassandra.exceptions.StartupException; |
59 | 62 | import org.apache.cassandra.io.filesystem.ForwardingFileSystem; |
60 | 63 | import org.apache.cassandra.io.filesystem.ForwardingFileSystemProvider; |
@@ -308,6 +311,38 @@ public void testKernelBug1057843Check() throws Exception |
308 | 311 | testKernelBug1057843Check("ext4", DiskAccessMode.mmap, new Semver("6.1.64.1-generic"), false); |
309 | 312 | } |
310 | 313 |
|
| 314 | + @SuppressWarnings("unchecked") |
| 315 | + @Test |
| 316 | + public void testErrorneousCustomCheckFailsStartup() |
| 317 | + { |
| 318 | + // ServiceLoader instantiates providers lazily: a custom StartupCheck whose no-arg |
| 319 | + // constructor throws does NOT fail ServiceLoader.load(), it fails later, during |
| 320 | + // iteration, surfacing as a ServiceConfigurationError from the iterator. We model that |
| 321 | + // here by having the iterator throw on next(). withServiceLoaderTests() must catch the |
| 322 | + // error around the iteration loop and rethrow it as a ConfigurationException, rather than |
| 323 | + // letting it escape uncaught (which would be wrapped by applyStartupChecks() into a |
| 324 | + // misleading "Invalid configuration of startup_checks" failure). |
| 325 | + ServiceConfigurationError error = new ServiceConfigurationError("org.example.BadCheck could not be instantiated", |
| 326 | + new RuntimeException("Failure to instantiate")); |
| 327 | + |
| 328 | + Iterator<StartupCheck> failingIterator = mock(Iterator.class); |
| 329 | + when(failingIterator.hasNext()).thenReturn(true); |
| 330 | + when(failingIterator.next()).thenThrow(error); |
| 331 | + |
| 332 | + ServiceLoader<StartupCheck> loader = mock(ServiceLoader.class); |
| 333 | + doReturn(failingIterator).when(loader).iterator(); |
| 334 | + |
| 335 | + try (MockedStatic<ServiceLoader> serviceLoader = Mockito.mockStatic(ServiceLoader.class)) |
| 336 | + { |
| 337 | + serviceLoader.when(() -> ServiceLoader.load(StartupCheck.class)).thenReturn(loader); |
| 338 | + |
| 339 | + assertThatExceptionOfType(ConfigurationException.class) |
| 340 | + .isThrownBy(() -> new StartupChecks().withDefaultTests().withServiceLoaderTests()) |
| 341 | + .withMessageContaining("Unable to get startup checks via ServiceLoader") |
| 342 | + .withCause(error); |
| 343 | + } |
| 344 | + } |
| 345 | + |
311 | 346 | @SuppressWarnings("unchecked") |
312 | 347 | @Test |
313 | 348 | public void testExternalCheckIsLoaded() throws StartupException |
|
0 commit comments