2727import hudson .console .AnnotatedLargeText ;
2828import hudson .console .HyperlinkNote ;
2929import hudson .model .TaskListener ;
30+ import hudson .remoting .Channel ;
3031import hudson .remoting .VirtualChannel ;
32+ import hudson .util .StreamTaskListener ;
3133import java .io .EOFException ;
3234import java .io .PrintWriter ;
3335import java .io .StringWriter ;
3739import java .util .Random ;
3840import java .util .concurrent .Callable ;
3941import java .util .function .BiFunction ;
42+ import java .util .logging .Formatter ;
43+ import java .util .logging .Handler ;
44+ import java .util .logging .Level ;
45+ import java .util .logging .LogRecord ;
46+ import java .util .logging .Logger ;
47+ import java .util .logging .SimpleFormatter ;
4048import jenkins .security .MasterToSlaveCallable ;
4149import org .apache .commons .io .FileUtils ;
4250import org .apache .commons .io .output .NullOutputStream ;
4351import org .apache .commons .io .output .NullWriter ;
4452import org .apache .commons .io .output .WriterOutputStream ;
53+ import static org .hamcrest .Matchers .*;
4554import org .jenkinsci .plugins .workflow .cps .CpsFlowDefinition ;
4655import org .jenkinsci .plugins .workflow .graph .FlowNode ;
4756import org .jenkinsci .plugins .workflow .job .WorkflowJob ;
5160import org .junit .ClassRule ;
5261import org .junit .Test ;
5362import org .jvnet .hudson .test .JenkinsRule ;
63+ import org .jvnet .hudson .test .LoggerRule ;
5464
5565/**
5666 * Foundation for compliance tests of {@link LogStorage} implementations.
@@ -63,6 +73,8 @@ public abstract class LogStorageTestBase {
6373
6474 @ ClassRule public static JenkinsRule r = new JenkinsRule ();
6575
76+ @ ClassRule public static LoggerRule logging = new LoggerRule ();
77+
6678 /** Create a new storage implementation, but potentially reusing any data initialized in the last {@link Before} setup. */
6779 protected abstract LogStorage createStorage () throws Exception ;
6880
@@ -142,6 +154,7 @@ protected static void close(TaskListener listener) throws Exception {
142154 }
143155
144156 @ Test public void remoting () throws Exception {
157+ logging .capture (100 ).record (Channel .class , Level .WARNING );
145158 LogStorage ls = createStorage ();
146159 TaskListener overall = ls .overallListener ();
147160 overall .getLogger ().println ("overall from master" );
@@ -150,12 +163,15 @@ protected static void close(TaskListener listener) throws Exception {
150163 long overallPos = assertOverallLog (0 , "overall from master\n <span class=\" pipeline-node-1\" >step from master\n </span>" , true );
151164 long stepPos = assertStepLog ("1" , 0 , "step from master\n " , true );
152165 VirtualChannel channel = r .createOnlineSlave ().getChannel ();
166+ channel .call (new RemoteLogDumper ("agent" ));
153167 channel .call (new RemotePrint ("overall from agent" , overall ));
154168 channel .call (new RemotePrint ("step from agent" , step ));
169+ channel .call (new GC ());
155170 overallPos = assertOverallLog (overallPos , "overall from agent\n <span class=\" pipeline-node-1\" >step from agent\n </span>" , true );
156171 stepPos = assertStepLog ("1" , stepPos , "step from agent\n " , true );
157172 assertEquals (overallPos , assertOverallLog (overallPos , "" , true ));
158173 assertEquals (stepPos , assertStepLog ("1" , stepPos , "" , true ));
174+ assertThat (logging .getMessages (), empty ());
159175 }
160176 private static final class RemotePrint extends MasterToSlaveCallable <Void , Exception > {
161177 static {
@@ -173,6 +189,39 @@ private static final class RemotePrint extends MasterToSlaveCallable<Void, Excep
173189 return null ;
174190 }
175191 }
192+ /** Checking behavior of {@link DelayBufferedOutputStream} garbage collection. */
193+ private static final class GC extends MasterToSlaveCallable <Void , Exception > {
194+ @ Override public Void call () throws Exception {
195+ System .gc ();
196+ System .runFinalization ();
197+ return null ;
198+ }
199+ }
200+ // TODO copied from pipeline-log-cloudwatch; consider whether this should be moved into LoggerRule
201+ private static final class RemoteLogDumper extends MasterToSlaveCallable <Void , RuntimeException > {
202+ private final String name ;
203+ private final TaskListener stderr = StreamTaskListener .fromStderr ();
204+ RemoteLogDumper (String name ) {
205+ this .name = name ;
206+ }
207+ @ Override public Void call () throws RuntimeException {
208+ Handler handler = new Handler () {
209+ final Formatter formatter = new SimpleFormatter ();
210+ @ Override public void publish (LogRecord record ) {
211+ if (isLoggable (record )) {
212+ stderr .getLogger ().print (formatter .format (record ).replaceAll ("(?m)^" , "[" + name + "] " ));
213+ }
214+ }
215+ @ Override public void flush () {}
216+ @ Override public void close () throws SecurityException {}
217+ };
218+ handler .setLevel (Level .ALL );
219+ Logger logger = Logger .getLogger (LogStorageTestBase .class .getPackage ().getName ());
220+ logger .setLevel (Level .FINER );
221+ logger .addHandler (handler );
222+ return null ;
223+ }
224+ }
176225
177226 /**
178227 * Checks what happens when code using {@link TaskListener#getLogger} prints a line with inadequate synchronization.
0 commit comments