From 05e6a867ca28b3a6ae249e3295a46b1cfca2a6a6 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Thu, 3 Sep 2026 09:27:08 -0400 Subject: [PATCH 1/2] Pin locale to deal with flakyness in GCS testing library --- .../solr/gcs/GCSBackupRepositoryTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java index e4a2e084445..3c9fd45d54c 100644 --- a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java +++ b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java @@ -36,6 +36,7 @@ import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; @@ -45,11 +46,31 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.core.backup.repository.BackupRepository; import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; /** Unit tests for {@link GCSBackupRepository} that use an in-memory Storage object */ public class GCSBackupRepositoryTest extends AbstractBackupRepositoryTest { + private static Locale savedLocale; + + @BeforeClass + public static void ensureCompatibleLocale() { + // Google's FakeStorageRpc (used internally by LocalStorageHelper) formats its own "now" + // timestamp using the JVM default Locale without forcing ASCII digits, then re-parses that + // same string with a strict RFC3339 parser. Locales with a non-Latin native numbering system + // (e.g. "dz" Dzongkha, "ar" Arabic) produce digits the parser can't read back, e.g. + // StorageException: Invalid date/time format: ༢༠༢༦-༠༩-༠༣T༡༢:༥༩:༤༣Z + // That's a bug in the test double, not in our code under test, so pin ROOT for this suite. + savedLocale = Locale.getDefault(); + Locale.setDefault(Locale.ROOT); + } + + @AfterClass + public static void restoreLocale() { + Locale.setDefault(savedLocale); + } + @AfterClass public static void tearDownClass() { LocalStorageGCSBackupRepository.clearStashedStorage(); From 17deeeaed3e1ce0e42401aa0d7f99bf85f80e34b Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Thu, 3 Sep 2026 09:50:38 -0400 Subject: [PATCH 2/2] Actually catch the error and then skip instead of probing to guess... --- .../solr/gcs/GCSBackupRepositoryTest.java | 47 ++++++++++--------- .../gcs/LocalStorageGCSBackupRepository.java | 15 +++--- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java index 3c9fd45d54c..68c825777ae 100644 --- a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java +++ b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/GCSBackupRepositoryTest.java @@ -36,7 +36,6 @@ import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; @@ -46,31 +45,12 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.core.backup.repository.BackupRepository; import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.Assume; import org.junit.Test; /** Unit tests for {@link GCSBackupRepository} that use an in-memory Storage object */ public class GCSBackupRepositoryTest extends AbstractBackupRepositoryTest { - private static Locale savedLocale; - - @BeforeClass - public static void ensureCompatibleLocale() { - // Google's FakeStorageRpc (used internally by LocalStorageHelper) formats its own "now" - // timestamp using the JVM default Locale without forcing ASCII digits, then re-parses that - // same string with a strict RFC3339 parser. Locales with a non-Latin native numbering system - // (e.g. "dz" Dzongkha, "ar" Arabic) produce digits the parser can't read back, e.g. - // StorageException: Invalid date/time format: ༢༠༢༦-༠༩-༠༣T༡༢:༥༩:༤༣Z - // That's a bug in the test double, not in our code under test, so pin ROOT for this suite. - savedLocale = Locale.getDefault(); - Locale.setDefault(Locale.ROOT); - } - - @AfterClass - public static void restoreLocale() { - Locale.setDefault(savedLocale); - } - @AfterClass public static void tearDownClass() { LocalStorageGCSBackupRepository.clearStashedStorage(); @@ -144,7 +124,7 @@ public void testCopyIndexFileToHandlesZeroByteReads() throws Exception { GCSBackupRepository repo = createRepositoryWithStorage(realStorage); URI sourceDir = repo.resolve(getBaseUri(), "backup"); BlobId blobId = BlobId.of(bucketName, sourceDir + "/source.dat"); - realStorage.create(BlobInfo.newBuilder(blobId).build(), data); + createBlob(realStorage, blobId, data); Storage zeroReturningStorage = createZeroReturningStorage(realStorage); GCSBackupRepository proxyRepo = createRepositoryWithStorage(zeroReturningStorage); @@ -171,7 +151,7 @@ public void testCopyIndexFileToCopiesFile() throws Exception { GCSBackupRepository repo = createRepositoryWithStorage(realStorage); URI sourceDir = repo.resolve(getBaseUri(), "backup"); BlobId blobId = BlobId.of(bucketName, sourceDir + "/source.dat"); - realStorage.create(BlobInfo.newBuilder(blobId).build(), data); + createBlob(realStorage, blobId, data); try (Directory dest = new ByteBuffersDirectory()) { repo.copyIndexFileTo(sourceDir, "source.dat", dest, "dest.dat"); @@ -184,6 +164,27 @@ public void testCopyIndexFileToCopiesFile() throws Exception { } } + /** + * Creates a blob, skipping (rather than failing) the test if the current default locale trips the + * known FakeStorageRpc/RFC3339 date-parsing bug - see {@link + * LocalStorageGCSBackupRepository#initializeBackupLocation()} for the same pattern. + */ + private static void createBlob(Storage storage, BlobId blobId, byte[] data) { + try { + storage.create(BlobInfo.newBuilder(blobId).build(), data); + } catch (Exception e) { + final Throwable cause = e.getCause(); + Assume.assumeFalse( + "This test uses a GCS mock library that is incompatible with the current default locale", + cause != null + && e instanceof StorageException + && cause.getMessage().contains("Invalid date/time format") + && cause instanceof NumberFormatException); + // Not the known locale incompatibility - a genuine failure, so don't swallow it. + throw new RuntimeException(e); + } + } + /** Storage proxy that fails on {@code reader} so we can assert copy errors are propagated. */ private static Storage createFailingStorage() { Storage delegate = LocalStorageHelper.customOptions(false).getService(); diff --git a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/LocalStorageGCSBackupRepository.java b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/LocalStorageGCSBackupRepository.java index fc26a188f87..da54e135de4 100644 --- a/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/LocalStorageGCSBackupRepository.java +++ b/solr/modules/gcs-repository/src/test/org/apache/solr/gcs/LocalStorageGCSBackupRepository.java @@ -96,13 +96,14 @@ protected void initializeBackupLocation() { createDirectory(baseLocationUri); } catch (Exception e) { final Throwable cause = e.getCause(); - if (cause != null) { - assumeFalse( - "This test uses a GCS mock library that is incompatible with the current default locale", - e instanceof StorageException - && cause.getMessage().contains("Invalid date/time format") - && cause instanceof NumberFormatException); - } + assumeFalse( + "This test uses a GCS mock library that is incompatible with the current default locale", + cause != null + && e instanceof StorageException + && cause.getMessage().contains("Invalid date/time format") + && cause instanceof NumberFormatException); + // Not the known locale incompatibility - a genuine failure, so don't swallow it. + throw new RuntimeException(e); } } }