1717
1818import static com .diffplug .spotless .GitPrePushHookInstaller .Executor .GRADLE ;
1919import static com .diffplug .spotless .GitPrePushHookInstaller .Executor .MAVEN ;
20+ import static java .util .stream .Collectors .toList ;
2021import static org .assertj .core .api .Assertions .assertThat ;
2122
22- import java .util .ArrayList ;
2323import java .util .List ;
24+ import java .util .concurrent .CopyOnWriteArrayList ;
25+ import java .util .stream .IntStream ;
2426
2527import org .junit .jupiter .api .AfterEach ;
2628import org .junit .jupiter .api .BeforeEach ;
3133class GitPrePushHookInstallerTest extends ResourceHarness {
3234 private final static String OS = System .getProperty ("os.name" );
3335
34- private final List <String > logs = new ArrayList <>();
36+ private final List <String > logs = new CopyOnWriteArrayList <>();
3537 private final GitPreHookLogger logger = new GitPreHookLogger () {
3638 @ Override
3739 public void info (String format , Object ... arguments ) {
@@ -68,9 +70,9 @@ public void should_not_create_pre_hook_file_when_git_is_not_installed() throws E
6870 gradle .install ();
6971
7072 // then
71- assertThat (logs ).hasSize ( 2 );
72- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
73- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git not found in root directory" );
73+ assertThat (logs ).containsExactly (
74+ "Installing git pre-push hook" ,
75+ "Git not found in root directory" );
7476 assertThat (newFile (".git/hooks/pre-push" )).doesNotExist ();
7577 }
7678
@@ -84,11 +86,11 @@ public void should_use_global_gradle_when_gradlew_is_not_installed() throws Exce
8486 gradle .install ();
8587
8688 // then
87- assertThat (logs ).hasSize ( 4 );
88- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
89- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook not found, creating it" );
90- assertThat ( logs ). element ( 2 ). isEqualTo ( "Local gradle wrapper (gradlew) not found, falling back to global command 'gradle'" );
91- assertThat ( logs ). element ( 3 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
89+ assertThat (logs ).containsExactly (
90+ "Installing git pre-push hook" ,
91+ "Git pre-push hook not found, creating it" ,
92+ "Local gradle wrapper (gradlew) not found, falling back to global command 'gradle'" ,
93+ "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
9294
9395 final var content = gradleHookContent ("git_pre_hook/pre-push.created-tpl" , ExecutorType .GLOBAL );
9496 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -108,10 +110,10 @@ public void should_reinstall_pre_hook_file_when_hook_already_installed() throws
108110 gradle .install ();
109111
110112 // then
111- assertThat (logs ).hasSize ( 3 );
112- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
113- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook already installed, reinstalling it" );
114- assertThat ( logs ). element ( 2 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + hookFile .getAbsolutePath ());
113+ assertThat (logs ).containsExactly (
114+ "Installing git pre-push hook" ,
115+ "Git pre-push hook already installed, reinstalling it" ,
116+ "Git pre-push hook installed successfully to the file " + hookFile .getAbsolutePath ());
115117
116118 final var content = gradleHookContent ("git_pre_hook/pre-push.existing-installed-end-tpl" , ExecutorType .WRAPPER );
117119 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -131,10 +133,10 @@ public void should_reinstall_pre_hook_file_when_hook_already_installed_in_the_mi
131133 gradle .install ();
132134
133135 // then
134- assertThat (logs ).hasSize ( 3 );
135- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
136- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook already installed, reinstalling it" );
137- assertThat ( logs ). element ( 2 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + hookFile .getAbsolutePath ());
136+ assertThat (logs ).containsExactly (
137+ "Installing git pre-push hook" ,
138+ "Git pre-push hook already installed, reinstalling it" ,
139+ "Git pre-push hook installed successfully to the file " + hookFile .getAbsolutePath ());
138140
139141 final var content = gradleHookContent ("git_pre_hook/pre-push.existing-reinstalled-middle-tpl" , ExecutorType .WRAPPER );
140142 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -171,10 +173,10 @@ public void should_create_pre_hook_file_when_hook_file_does_not_exists() throws
171173 gradle .install ();
172174
173175 // then
174- assertThat (logs ).hasSize ( 3 );
175- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
176- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook not found, creating it" );
177- assertThat ( logs ). element ( 2 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
176+ assertThat (logs ).containsExactly (
177+ "Installing git pre-push hook" ,
178+ "Git pre-push hook not found, creating it" ,
179+ "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
178180
179181 final var content = gradleHookContent ("git_pre_hook/pre-push.created-tpl" , ExecutorType .WRAPPER );
180182 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -192,9 +194,9 @@ public void should_append_to_existing_pre_hook_file_when_hook_file_exists() thro
192194 gradle .install ();
193195
194196 // then
195- assertThat (logs ).hasSize ( 2 );
196- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
197- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
197+ assertThat (logs ).containsExactly (
198+ "Installing git pre-push hook" ,
199+ "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
198200
199201 final var content = gradleHookContent ("git_pre_hook/pre-push.existing-installed-end-tpl" , ExecutorType .WRAPPER );
200202 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -211,10 +213,10 @@ public void should_create_pre_hook_file_for_maven_when_hook_file_does_not_exists
211213 gradle .install ();
212214
213215 // then
214- assertThat (logs ).hasSize ( 3 );
215- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
216- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook not found, creating it" );
217- assertThat ( logs ). element ( 2 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
216+ assertThat (logs ).containsExactly (
217+ "Installing git pre-push hook" ,
218+ "Git pre-push hook not found, creating it" ,
219+ "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
218220
219221 final var content = mavenHookContent ("git_pre_hook/pre-push.created-tpl" , ExecutorType .WRAPPER );
220222 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -230,11 +232,11 @@ public void should_use_global_maven_when_maven_wrapper_is_not_installed() throws
230232 gradle .install ();
231233
232234 // then
233- assertThat (logs ).hasSize ( 4 );
234- assertThat ( logs ). element ( 0 ). isEqualTo ( "Installing git pre-push hook" );
235- assertThat ( logs ). element ( 1 ). isEqualTo ( "Git pre-push hook not found, creating it" );
236- assertThat ( logs ). element ( 2 ). isEqualTo ( "Local maven wrapper (mvnw) not found, falling back to global command 'mvn'" );
237- assertThat ( logs ). element ( 3 ). isEqualTo ( "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
235+ assertThat (logs ).containsExactly (
236+ "Installing git pre-push hook" ,
237+ "Git pre-push hook not found, creating it" ,
238+ "Local maven wrapper (mvnw) not found, falling back to global command 'mvn'" ,
239+ "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
238240
239241 final var content = mavenHookContent ("git_pre_hook/pre-push.created-tpl" , ExecutorType .GLOBAL );
240242 assertFile (".git/hooks/pre-push" ).hasContent (content );
@@ -334,6 +336,30 @@ public void should_use_gradle_global_when_bat_and_cmd_files_not_exists_for_windo
334336 assertThat (hook ).contains ("SPOTLESS_EXECUTOR=gradle" );
335337 }
336338
339+ @ Test
340+ public void should_handle_parallel_installation () {
341+ // given
342+ setFile (".git/config" ).toContent ("" );
343+
344+ // when
345+ parallelRun (() -> {
346+ final var gradle = new GitPrePushHookInstallerGradle (logger , rootFolder ());
347+ gradle .install ();
348+ });
349+
350+ // then
351+ assertThat (logs ).containsExactlyInAnyOrder (
352+ "Installing git pre-push hook" ,
353+ "Git pre-push hook not found, creating it" ,
354+ "Parallel Spotless Git pre-push hook installation detected, skipping installation" ,
355+ "Parallel Spotless Git pre-push hook installation detected, skipping installation" ,
356+ "Local gradle wrapper (gradlew) not found, falling back to global command 'gradle'" ,
357+ "Git pre-push hook installed successfully to the file " + newFile (".git/hooks/pre-push" ).getAbsolutePath ());
358+
359+ final var content = gradleHookContent ("git_pre_hook/pre-push.created-tpl" , ExecutorType .GLOBAL );
360+ assertFile (".git/hooks/pre-push" ).hasContent (content );
361+ }
362+
337363 private String gradleHookContent (String resourcePath , ExecutorType executorType ) {
338364 return getTestResource (resourcePath )
339365 .replace ("${executor}" , executorType == ExecutorType .WRAPPER ? "./" + newFile ("gradlew" ).getName () : "gradle" )
@@ -348,7 +374,31 @@ private String mavenHookContent(String resourcePath, ExecutorType executorType)
348374 .replace ("${applyCommand}" , "spotless:apply" );
349375 }
350376
377+ private void parallelRun (ThrowableRun runnable ) {
378+ IntStream .range (0 , 3 )
379+ .mapToObj (i -> new Thread (() -> {
380+ try {
381+ runnable .run ();
382+ } catch (Exception e ) {
383+ throw new RuntimeException (e );
384+ }
385+ }))
386+ .peek (Thread ::start )
387+ .collect (toList ())
388+ .forEach (t -> {
389+ try {
390+ t .join ();
391+ } catch (InterruptedException e ) {
392+ throw new RuntimeException (e );
393+ }
394+ });
395+ }
396+
351397 private enum ExecutorType {
352398 WRAPPER , GLOBAL
353399 }
400+
401+ private interface ThrowableRun {
402+ void run () throws Exception ;
403+ }
354404}
0 commit comments