Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions metadata/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@
"allowed-packages" : [ "org.quartz" ],
"directory" : "org.quartz-scheduler/quartz",
"module" : "org.quartz-scheduler:quartz"
}, {
"allowed-packages" : [ "org.slf4j" ],
"directory" : "org.slf4j/slf4j-api",
"module" : "org.slf4j:slf4j-api"
}, {
"allowed-packages" : [ "org.testcontainers" ],
"directory" : "org.testcontainers/testcontainers",
Expand Down
5 changes: 5 additions & 0 deletions metadata/org.slf4j/slf4j-api/1.7.36/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"reflect-config.json",
"resource-config.json",
"serialization-config.json"
]
13 changes: 13 additions & 0 deletions metadata/org.slf4j/slf4j-api/1.7.36/reflect-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"condition": {
"typeReachable": "org.slf4j.impl.StaticLoggerBinder"
},
"name": "java.util.concurrent.atomic.AtomicReference",
"fields": [
{
"name": "value"
}
]
}
]
8 changes: 8 additions & 0 deletions metadata/org.slf4j/slf4j-api/1.7.36/resource-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"resources":{
"includes":[{
"condition":{"typeReachable":"org.slf4j.LoggerFactory"},
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
}]},
"bundles":[]
}
17 changes: 17 additions & 0 deletions metadata/org.slf4j/slf4j-api/1.7.36/serialization-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"types": [
{
"name": "org.slf4j.helpers.BasicMarker"
},
{
"name": "java.lang.String"
},
{
"name": "java.util.concurrent.CopyOnWriteArrayList"
}
],
"lambdaCapturingTypes": [
],
"proxies": [
]
}
10 changes: 10 additions & 0 deletions metadata/org.slf4j/slf4j-api/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"latest": true,
"metadata-version": "1.7.36",
"module": "org.slf4j:slf4j-api",
"tested-versions": [
"1.7.36"
]
}
]
6 changes: 6 additions & 0 deletions tests/src/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,12 @@
"name" : "org.quartz-scheduler:quartz",
"versions" : [ "2.3.2" ]
} ]
}, {
"test-project-path" : "org.slf4j/slf4j-api/1.7.36",
"libraries" : [ {
"name" : "org.slf4j:slf4j-api",
"versions" : [ "1.7.36" ]
} ]
}, {
"test-project-path" : "org.testcontainers/testcontainers/1.17.6",
"libraries" : [ {
Expand Down
4 changes: 4 additions & 0 deletions tests/src/org.slf4j/slf4j-api/1.7.36/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
gradlew.bat
gradlew
gradle/
build/
21 changes: 21 additions & 0 deletions tests/src/org.slf4j/slf4j-api/1.7.36/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
plugins {
id 'org.graalvm.internal.tck'
}

String libraryVersion = tck.testedLibraryVersion.get()

dependencies {
implementation "org.slf4j:slf4j-api:$libraryVersion"
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}

graalvmNative {
agent {
defaultMode = "conditional"
modes {
conditional {
userCodeFilterPath = "user-code-filter.json"
}
}
}
}
2 changes: 2 additions & 0 deletions tests/src/org.slf4j/slf4j-api/1.7.36/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
library.version = 1.7.36
metadata.dir = org.slf4j/slf4j-api/1.7.36/
13 changes: 13 additions & 0 deletions tests/src/org.slf4j/slf4j-api/1.7.36/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pluginManagement {
def tckPath = Objects.requireNonNullElse(
System.getenv("GVM_TCK_TCKDIR"),
"../../../../tck-build-logic"
)
includeBuild(tckPath)
}

plugins {
id "org.graalvm.internal.tck-settings" version "1.0.0-SNAPSHOT"
}

rootProject.name = 'org.slf4j.slf4j-api_tests'
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package org_slf4j.slf4j_api;

import org.junit.jupiter.api.Test;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.slf4j.helpers.NOPLogger;
import org.slf4j.helpers.NOPLoggerFactory;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.jupiter.api.Assertions.*;

public class Slf4j_apiTest {

@Test
void iLoggerFactoryDefaultsToNOPWhenNoBinding() {
ILoggerFactory factory = LoggerFactory.getILoggerFactory();
assertNotNull(factory, "ILoggerFactory should not be null");
assertTrue(factory instanceof NOPLoggerFactory,
"Without a binding on the classpath, factory should be NOPLoggerFactory");

Logger logger = LoggerFactory.getLogger("test");
assertNotNull(logger);
assertEquals("NOP", logger.getName());
assertTrue(logger instanceof NOPLogger, "Logger should be NOPLogger under NOP factory");

// NOP should report all levels disabled
assertFalse(logger.isTraceEnabled());
assertFalse(logger.isDebugEnabled());
assertFalse(logger.isInfoEnabled());
assertFalse(logger.isWarnEnabled());
assertFalse(logger.isErrorEnabled());

// All logging calls should be safe no-ops
logger.trace("trace {}", 123);
logger.debug("debug {}", 123);
logger.info("info {}", 123);
logger.warn("warn {}", 123);
logger.error("error {}", 123);
logger.error("error with throwable", new RuntimeException("boom"));
}

@Test
void mdcIsNoOpWithoutBinding_andIsThreadLocal() throws Exception {
// Make sure MDC starts clean
MDC.clear();

// With NOP adapter, put/get should be no-ops
MDC.put("foo", "bar");
assertNull(MDC.get("foo"), "MDC.get should return null with NOP adapter");
assertNull(MDC.getCopyOfContextMap(), "MDC.getCopyOfContextMap should be null with NOP adapter");

Map<String, String> m = new HashMap<>();
m.put("a", "b");
MDC.setContextMap(m);
assertNull(MDC.get("a"), "MDC.setContextMap should be a no-op with NOP adapter");
assertNull(MDC.getCopyOfContextMap());

// Verify MDC operations are thread-local (even though they are no-ops)
AtomicReference<Throwable> failure = new AtomicReference<>();
Thread t = new Thread(() -> {
try {
MDC.put("k", "v");
assertNull(MDC.get("k"), "MDC in a different thread should also be no-op");
MDC.clear();
assertNull(MDC.get("k"));
} catch (Throwable ex) {
failure.set(ex);
}
});
t.start();
t.join();
if (failure.get() != null) {
throw new AssertionError("Failure in MDC thread", failure.get());
}

// Main thread should still be unaffected
assertNull(MDC.get("k"));
MDC.clear();
}

@Test
void markerFactoryFallsBackToBasic_andSupportsOperations() {
// Without a StaticMarkerBinder, MarkerFactory should fall back to BasicMarkerFactory
Object markerFactory = MarkerFactory.getIMarkerFactory();
assertEquals("org.slf4j.helpers.BasicMarkerFactory", markerFactory.getClass().getName(),
"MarkerFactory should use BasicMarkerFactory when no binding is present");

Marker parent = MarkerFactory.getMarker("PARENT");
Marker child = MarkerFactory.getMarker("CHILD");

assertEquals("PARENT", parent.getName());
assertFalse(parent.hasReferences());
assertFalse(parent.contains(child));
assertFalse(parent.contains("CHILD"));

parent.add(child);
assertTrue(parent.hasReferences());
assertTrue(parent.contains(child));
assertTrue(parent.contains("CHILD"));

// Remove reference
assertTrue(parent.remove(child));
assertFalse(parent.hasReferences());
assertFalse(parent.contains(child));

// Detach from factory cache and ensure new instance is provided afterward
Marker d1 = MarkerFactory.getMarker("DETACH_ME");
boolean detached = MarkerFactory.getIMarkerFactory().detachMarker("DETACH_ME");
// Detach may return false if not present; still, next get should provide an instance which may differ.
Marker d2 = MarkerFactory.getMarker("DETACH_ME");
// After a detach call, BasicMarkerFactory should return a fresh instance for the name
assertNotSame(d1, d2, "Detached marker should not be the same instance as the newly created marker");
}

@Test
void markerSerializationRoundTrip_preservesNameAndReferences() throws Exception {
Marker a = MarkerFactory.getMarker("A");
Marker b = MarkerFactory.getMarker("B");
a.add(b);

Marker a2 = roundTrip(a);
assertNotNull(a2);
assertEquals("A", a2.getName());
// BasicMarker implements contains(String) based on reference names
assertTrue(a2.contains("B"), "Deserialized marker should preserve references");
}

@Test
void loggingWithMarkersAndParametersDoesNotThrow() {
Logger logger = LoggerFactory.getLogger(Slf4j_apiTest.class);
Marker marker = MarkerFactory.getMarker("M");

logger.trace(marker, "trace {}", 1);
logger.debug(marker, "debug {} {}", "x", 2);
logger.info(marker, "info");
logger.warn(marker, "warn with throwable", new IllegalStateException("warn"));
logger.error(marker, "error {} and {}", "A", "B");
logger.error(marker, "error with throwable {}", 3, new RuntimeException("boom"));

// Also call level-checks with marker (NOP returns false)
assertFalse(logger.isTraceEnabled(marker));
assertFalse(logger.isDebugEnabled(marker));
assertFalse(logger.isInfoEnabled(marker));
assertFalse(logger.isWarnEnabled(marker));
assertFalse(logger.isErrorEnabled(marker));
}

@SuppressWarnings("unchecked")
private static <T extends Serializable> T roundTrip(T obj) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
}
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
return (T) ois.readObject();
}
}
}
10 changes: 10 additions & 0 deletions tests/src/org.slf4j/slf4j-api/1.7.36/user-code-filter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"rules" : [
{
"excludeClasses" : "**"
},
{
"includeClasses" : "org.slf4j.**"
}
]
}