1+
2+ package io .gdcc .spi .export ;
3+
4+ import io .gdcc .spi .export .fixtures .StubDdiExporter ;
5+ import io .gdcc .spi .export .fixtures .StubJsonExporter ;
6+ import io .gdcc .spi .meta .descriptor .Descriptor ;
7+ import io .gdcc .spi .meta .descriptor .DescriptorFormat ;
8+ import org .junit .jupiter .api .Test ;
9+
10+ import java .io .IOException ;
11+ import java .io .InputStream ;
12+ import java .nio .charset .StandardCharsets ;
13+
14+ import static org .junit .jupiter .api .Assertions .assertEquals ;
15+ import static org .junit .jupiter .api .Assertions .assertNotNull ;
16+ import static org .junit .jupiter .api .Assertions .assertNull ;
17+ import static org .junit .jupiter .api .Assertions .assertTrue ;
18+
19+ /**
20+ * Verifies that the annotation processor generates correct descriptors and service files
21+ * when compiling real Exporter SPI implementations.
22+ *
23+ * <p>The test implementation classes in this package are compiled with the processor on the
24+ * classpath. The processor writes descriptors and service files into {@code target/test-classes/},
25+ * which this test reads at runtime to verify correctness.</p>
26+ */
27+ class ExporterImplTest {
28+
29+ @ Test
30+ void generatesDescriptorAndServiceFileForBaseExporterImplementation () throws IOException {
31+ Class <?> implClass = StubJsonExporter .class ;
32+
33+ Descriptor descriptor = readDescriptor (implClass );
34+ assertNotNull (descriptor , "Descriptor should be generated for " + implClass );
35+
36+ assertEquals (DescriptorFormat .transformClassName (implClass ), descriptor .klass ());
37+ assertEquals (Exporter .class .getCanonicalName (), descriptor .kind ());
38+ assertEquals (Exporter .API_LEVEL , descriptor .contractLevel (Exporter .class .getCanonicalName ()));
39+ assertEquals (ExportDataProvider .API_LEVEL , descriptor .requiredProviderLevel (ExportDataProvider .class .getCanonicalName ()));
40+
41+ String serviceFile = readServiceFile (Exporter .class );
42+ assertNotNull (serviceFile , "Service file should be generated for Exporter" );
43+ assertTrue (serviceFile .contains (DescriptorFormat .transformClassName (implClass )), "Service file should contain " + implClass );
44+ }
45+
46+ @ Test
47+ void generatesDescriptorWithBaseAndCapabilityForXmlExporterImplementation () throws IOException {
48+ Class <?> implClass = StubDdiExporter .class ;
49+
50+ Descriptor descriptor = readDescriptor (implClass );
51+ assertNotNull (descriptor , "Descriptor should be generated for " + implClass );
52+
53+ assertEquals (DescriptorFormat .transformClassName (implClass ), descriptor .klass ());
54+ assertEquals (Exporter .class .getCanonicalName (), descriptor .kind ());
55+ assertEquals (Exporter .API_LEVEL , descriptor .contractLevel (Exporter .class .getCanonicalName ()));
56+ assertEquals (XMLExporter .API_LEVEL , descriptor .contractLevel (XMLExporter .class .getCanonicalName ()));
57+ assertEquals (ExportDataProvider .API_LEVEL , descriptor .requiredProviderLevel (ExportDataProvider .class .getCanonicalName ()));
58+
59+ String serviceFile = readServiceFile (Exporter .class );
60+ assertNotNull (serviceFile , "Service file should be generated for Exporter" );
61+ assertTrue (serviceFile .contains (DescriptorFormat .transformClassName (implClass )), "Service file should contain " + implClass );
62+ }
63+
64+ @ Test
65+ void doesNotGenerateServiceFileForXmlExporterCapability () {
66+ String serviceFile = readServiceFile (XMLExporter .class );
67+ assertNull (serviceFile , "Service file must never be generated for capability contract XMLExporter" );
68+ }
69+
70+ @ Test
71+ void xmlExporterDefaultMediaTypeSatisfiesBaseContract () throws IOException {
72+ // StubDdiExporter implements XMLExporter (which extends Exporter) and does NOT
73+ // override getMediaType(). Because XMLExporter extends Exporter in the Java type
74+ // hierarchy, the default on XMLExporter satisfies the abstract declaration on Exporter.
75+ // If this were not the case, compilation would have failed and no descriptor would exist.
76+ Class <?> implClass = StubDdiExporter .class ;
77+
78+ Descriptor descriptor = readDescriptor (implClass );
79+ assertNotNull (descriptor , "Descriptor should exist, proving compilation succeeded without explicit getMediaType() override" );
80+ }
81+
82+ // ── Helpers ─────────────────────────────────────────────────────────────────
83+
84+ private Descriptor readDescriptor (Class implClass ) throws IOException {
85+ String resourcePath = DescriptorFormat .toPath (implClass );
86+ try (InputStream is = getClass ().getClassLoader ().getResourceAsStream (resourcePath )) {
87+ if (is == null ) {
88+ return null ;
89+ }
90+ return DescriptorFormat .read (new String (is .readAllBytes (), StandardCharsets .UTF_8 ));
91+ }
92+ }
93+
94+ private String readServiceFile (Class <?> serviceType ) {
95+ String resourcePath = "META-INF/services/" + serviceType .getName ();
96+ try (InputStream is = getClass ().getClassLoader ().getResourceAsStream (resourcePath )) {
97+ if (is == null ) {
98+ return null ;
99+ }
100+ return new String (is .readAllBytes (), StandardCharsets .UTF_8 );
101+ } catch (IOException e ) {
102+ return null ;
103+ }
104+ }
105+ }
0 commit comments