1919import java .io .IOException ;
2020import java .nio .charset .StandardCharsets ;
2121import java .nio .file .Files ;
22+ import java .nio .file .NoSuchFileException ;
2223import java .nio .file .Path ;
2324import java .nio .file .Paths ;
2425import java .util .Arrays ;
26+ import java .util .HashMap ;
2527import java .util .LinkedHashMap ;
2628import java .util .LinkedHashSet ;
2729import java .util .Map ;
2830import java .util .Set ;
2931import java .util .function .UnaryOperator ;
32+ import java .util .stream .Collectors ;
3033
3134import org .gradle .api .tasks .SourceSet ;
3235import org .gradle .testkit .runner .BuildResult ;
4144import org .junit .jupiter .params .provider .EnumSource ;
4245
4346import org .springframework .boot .build .architecture .annotations .TestConditionalOnClass ;
47+ import org .springframework .boot .build .architecture .annotations .TestDeprecatedConfigurationProperty ;
4448import org .springframework .util .ClassUtils ;
4549import org .springframework .util .FileSystemUtils ;
4650import org .springframework .util .StringUtils ;
@@ -316,6 +320,16 @@ void whenConditionalOnClassUsedOnBeanMethodsWithTestSourcesShouldSucceedAndWrite
316320 build (gradleBuild , Task .CHECK_ARCHITECTURE_TEST );
317321 }
318322
323+ @ Test
324+ void whenDeprecatedConfigurationPropertyIsMissingSinceShouldFailAndWriteReport () throws IOException {
325+ prepareTask (Task .CHECK_ARCHITECTURE_MAIN , "configurationproperties" , "annotations" );
326+ GradleBuild gradleBuild = this .gradleBuild .withDependencies (SPRING_CONTEXT )
327+ .withDeprecatedConfigurationPropertyAnnotation (TestDeprecatedConfigurationProperty .class .getName ());
328+ buildAndFail (gradleBuild , Task .CHECK_ARCHITECTURE_MAIN ,
329+ "should include a non-empty 'since' attribute of @DeprecatedConfigurationProperty" ,
330+ "DeprecatedConfigurationPropertySince.getProperty" );
331+ }
332+
319333 private void prepareTask (Task task , String ... sourceDirectories ) throws IOException {
320334 for (String sourceDirectory : sourceDirectories ) {
321335 FileSystemUtils .copyRecursively (
@@ -348,7 +362,12 @@ private void buildAndFail(GradleBuild gradleBuild, Task task, String... messages
348362 try {
349363 BuildResult buildResult = gradleBuild .buildAndFail (task .toString ());
350364 assertThat (buildResult .taskPaths (TaskOutcome .FAILED )).as (buildResult .getOutput ()).contains (":" + task );
351- assertThat (task .getFailureReport (gradleBuild .getProjectDir ())).contains (messages );
365+ try {
366+ assertThat (task .getFailureReport (gradleBuild .getProjectDir ())).contains (messages );
367+ }
368+ catch (NoSuchFileException ex ) {
369+ throw new AssertionError ("Expected failure report not found\n " + buildResult .getOutput ());
370+ }
352371 }
353372 catch (UnexpectedBuildSuccess ex ) {
354373 throw new AssertionError ("Expected build to fail but it succeeded\n " + ex .getBuildResult ().getOutput (), ex );
@@ -410,9 +429,18 @@ GradleBuild withProhibitObjectsRequireNonNull(Boolean prohibitObjectsRequireNonN
410429 return this ;
411430 }
412431
413- GradleBuild withConditionalOnClassAnnotation (String annotationName ) {
432+ GradleBuild withConditionalOnClassAnnotation (String annotationClass ) {
433+ for (Task task : Task .values ()) {
434+ configureTask (task , (configuration ) -> configuration
435+ .withAnnotation (ArchitectureCheck .CONDITIONAL_ON_CLASS , annotationClass ));
436+ }
437+ return this ;
438+ }
439+
440+ GradleBuild withDeprecatedConfigurationPropertyAnnotation (String annotationClass ) {
414441 for (Task task : Task .values ()) {
415- configureTask (task , (configuration ) -> configuration .withConditionalOnClassAnnotation (annotationName ));
442+ configureTask (task , (configuration ) -> configuration
443+ .withAnnotation (ArchitectureCheck .DEPRECATED_CONFIGURATION_PROPERTY , annotationClass ));
416444 }
417445 return this ;
418446 }
@@ -454,18 +482,18 @@ private GradleRunner prepareRunner(String... arguments) throws IOException {
454482 for (String dependency : this .dependencies ) {
455483 buildFile .append ("\n implementation " ).append (StringUtils .quote (dependency ));
456484 }
457- buildFile .append ("} \n " );
485+ buildFile .append ("\n } \n \n " );
458486 }
459487 this .taskConfigurations .forEach ((task , configuration ) -> {
460488 buildFile .append (task ).append (" {" );
461- if (configuration .conditionalOnClassAnnotation () != null ) {
462- buildFile .append ("\n conditionalOnClassAnnotation = " )
463- .append (StringUtils .quote (configuration .conditionalOnClassAnnotation ()));
464- }
465489 if (configuration .prohibitObjectsRequireNonNull () != null ) {
466490 buildFile .append ("\n prohibitObjectsRequireNonNull = " )
467491 .append (configuration .prohibitObjectsRequireNonNull ());
468492 }
493+ if (configuration .annotations () != null && !configuration .annotations ().isEmpty ()) {
494+ buildFile .append ("\n annotationClasses = " )
495+ .append (toGroovyMapString (configuration .annotations ()));
496+ }
469497 buildFile .append ("\n }\n " );
470498 });
471499 Files .writeString (this .projectDir .resolve ("build.gradle" ), buildFile , StandardCharsets .UTF_8 );
@@ -475,15 +503,31 @@ private GradleRunner prepareRunner(String... arguments) throws IOException {
475503 .withPluginClasspath ();
476504 }
477505
478- private record TaskConfiguration (Boolean prohibitObjectsRequireNonNull , String conditionalOnClassAnnotation ) {
506+ static String toGroovyMapString (Map <String , String > map ) {
507+ return map .entrySet ()
508+ .stream ()
509+ .map ((entry ) -> "'" + entry .getKey () + "' : '" + entry .getValue () + "'" )
510+ .collect (Collectors .joining (", " , "[" , "]" ));
511+ }
479512
480- private TaskConfiguration withConditionalOnClassAnnotation (String annotationName ) {
481- return new TaskConfiguration (this .prohibitObjectsRequireNonNull , annotationName );
513+ private record TaskConfiguration (Boolean prohibitObjectsRequireNonNull , Map <String , String > annotations ) {
514+
515+ public TaskConfiguration {
516+ if (annotations == null ) {
517+ annotations = new HashMap <>();
518+ }
482519 }
483520
484521 private TaskConfiguration withProhibitObjectsRequireNonNull (Boolean prohibitObjectsRequireNonNull ) {
485- return new TaskConfiguration (prohibitObjectsRequireNonNull , this .conditionalOnClassAnnotation );
522+ return new TaskConfiguration (prohibitObjectsRequireNonNull , this .annotations );
486523 }
524+
525+ private TaskConfiguration withAnnotation (String name , String annotationClass ) {
526+ Map <String , String > map = new HashMap <>(this .annotations );
527+ map .put (name , annotationClass );
528+ return new TaskConfiguration (this .prohibitObjectsRequireNonNull , map );
529+ }
530+
487531 }
488532
489533 }
0 commit comments