Skip to content

Commit

Permalink
Longer timeouts for test runs (#80)
Browse files Browse the repository at this point in the history
RemoteLogger open and close are synchronous
ClientLogger can be used for testing
  • Loading branch information
bmeike authored Oct 31, 2024
1 parent d3c4bbb commit 60b0dc5
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 45 deletions.
4 changes: 3 additions & 1 deletion environment/LogSlurp/ClientLogger/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using var httpClient = new HttpClient();
var log_id = Guid.NewGuid().ToString();
var response = await httpClient.PostAsync($"http://localhost:{Port}/startNewLog", JsonContent.Create(new { log_id }));
Console.WriteLine();
Console.WriteLine("Session ID: " + log_id);

var ws = new ClientWebSocket();
ws.Options.SetRequestHeader("CBL-Log-ID", log_id);
Expand All @@ -31,4 +33,4 @@
var logString = await httpClient.GetStringAsync($"http://localhost:{Port}/retrieveLog");
Console.WriteLine();
Console.WriteLine("==== Retrieved ====");
Console.WriteLine(logString);
Console.WriteLine(logString);
10 changes: 10 additions & 0 deletions environment/LogSlurp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,13 @@ quit<br />
<br />
==== Retrieved ====<br />
ClientLogger: 2024-08-12 23:33:34,147 hello

The client logger can be used to test the implementation of a logger. Do this:
- Start the log slurper
- Start the client logger. It will print the id of the session it started with the slurper
- When the client logger loops asking for log messages let it sit
- Run your logger implementation. Use the session id from the client logger and an arbitrary "tag"
- Log a few things from your implementation. Maybe type a couple of log messages at the client logger as well
- When you have enough logs to validate your implementation, type "quit" at the client logger
- The client logger will display the contents of the session, including the log messages you typed at it, along with the ones sent from your logger implementation, interleaved approprately.

2 changes: 1 addition & 1 deletion jenkins/pipelines/android/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pipeline {
string(name: 'CBL_BUILD', defaultValue: '', description: 'Couchbase Lite Build Number')
string(name: 'SGW_URL', defaultValue: '', description: "The url of Sync Gateway to download")
}
options { timeout(time: 30, unit: 'MINUTES') }
options { timeout(time: 60, unit: 'MINUTES') }
stages {
stage('Init') {
steps {
Expand Down
2 changes: 1 addition & 1 deletion jenkins/pipelines/java/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pipeline {
string(name: 'CBL_BUILD', defaultValue: '', description: 'Couchbase Lite Build Number')
string(name: 'SGW_URL', defaultValue: '', description: "The url of Sync Gateway to download")
}
options { timeout(time: 60, unit: 'MINUTES') }
options { timeout(time: 120, unit: 'MINUTES') }
stages {
stage('Init') {
steps {
Expand Down
2 changes: 1 addition & 1 deletion jenkins/pipelines/java/desktop/win_tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ param (
[string]$version,

[Parameter(Mandatory=$true)]
[string]$buildNumber
[string]$buildNumber,

[Parameter(Mandatory=$false)]
[string]$sgUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
//
package com.couchbase.lite.mobiletest.services;

import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -37,7 +39,46 @@
@SuppressWarnings({"PMD.UnusedPrivateField", "PMD.SingularField"})
public class RemoteLogger extends Log.TestLogger {
private static final String TAG = "REMLOG";
private static final long TIMEOUT_SECS = 30;

@NonNull
private final WebSocketListener listener = new WebSocketListener() {
@Override
public void onOpen(@NonNull WebSocket socket, @NonNull Response resp) {
if (!(resp.isSuccessful() || (resp.code() == 101))) { fail("Failed starting new log: " + resp.code()); }
final WebSocket oSocket = webSocket.getAndSet(socket);
startLatch.countDown();
if (oSocket != null) { fail("Unexpected WebSocket open"); }
}

@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
Log.p(TAG, "Unexpected message from LogSlurper: " + text);
}

@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response resp) {
stopLatch.countDown();
startLatch.countDown();
fail("WebSocket error", t);
}

@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
stopLatch.countDown();
startLatch.countDown();
close(1000, "Closed");
}
};

@NonNull
private final CountDownLatch startLatch = new CountDownLatch(1);
@NonNull
private final CountDownLatch stopLatch = new CountDownLatch(1);
@NonNull
private final AtomicReference<WebSocket> webSocket = new AtomicReference<>();
@NonNull
private final AtomicBoolean connected = new AtomicBoolean();

@NonNull
private final String url;
Expand All @@ -46,50 +87,34 @@ public class RemoteLogger extends Log.TestLogger {
@NonNull
private final String tag;

@Nullable
@GuardedBy("url")
private WebSocket webSocket;

public RemoteLogger(@NonNull String url, @NonNull String sessionId, @NonNull String tag) {
this.url = url;
this.sessionId = sessionId;
this.tag = tag;
}

// Synchronously open a connection to the remote log server.
public void connect() {
if (connected.getAndSet(true)) { throw new ServerError("Attempt to reused a RemoteLogger"); }

new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.build()
.newWebSocket(
new Request.Builder()
.url("\"ws://" + url + "/openLogStream")
.url("http://" + url + "/openLogStream")
.header("CBL-Log-ID", sessionId)
.header("CBL-Log-Tag", tag)
.get()
.build(),
new WebSocketListener() {
@Override
public void onOpen(@NonNull WebSocket socket, @NonNull Response resp) {
if (!resp.isSuccessful()) {
fail("Failed starting new log response: " + resp.code());
}
synchronized (url) { webSocket = socket; }
}

@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
fail("Unexpected message from LogSlurper: " + text);
}

@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response resp) {
fail("WebSocket error: " + t.getMessage(), t);
close();
}

@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { close(); }
});
listener);

try {
if (startLatch.await(10, TimeUnit.SECONDS)) { return; }
}
catch (InterruptedException ignore) { }
fail("Failed opening LogSlurper websocket");
}

@Override
Expand All @@ -99,37 +124,45 @@ public void log(@NonNull LogLevel level, @NonNull LogDomain domain, @NonNull Str

@Override
public void log(LogLevel level, String tag, String msg, Exception err) {
final WebSocket socket;
synchronized (url) { socket = webSocket; }

final WebSocket socket = webSocket.get();
if (socket == null) {
Log.p(TAG, "RemoteLogger is not connected");
return;
}

final StringBuilder logMsg = new StringBuilder();
logMsg.append(tag).append('/').append(level.toString()).append(' ').append(msg);
sendLogMessage(socket, new StringBuilder(tag).append('/').append(level).append(' ').append(msg).toString());

if (err != null) {
final StringWriter sw = new StringWriter();
err.printStackTrace(new PrintWriter(sw));
logMsg.append(System.lineSeparator()).append(sw);
sendLogMessage(socket, sw.toString());
}

socket.send(logMsg.toString());
}

@Override
public void close() {
final WebSocket socket;
synchronized (url) { socket = webSocket; }
if (socket != null) { socket.close(1000, null); }
public void close() { close(1001, "Closed by client"); }

private void sendLogMessage(@NonNull WebSocket socket, @NonNull String message) {
if (!socket.send(message)) { Log.p(TAG, "Failed to send log message"); }
}

private void fail(String message) { fail(message, null); }

private void fail(String message, Throwable e) {
close();
close(1011, message);
throw new ServerError(message, e);
}

// Synchronously close the connection to the remote log server.
private void close(int code, @NonNull String reason) {
final WebSocket socket = webSocket.getAndSet(null);
if (socket == null) { return; }

socket.close(code, reason);
try {
if (stopLatch.await(TIMEOUT_SECS, TimeUnit.SECONDS)) { return; }
}
catch (InterruptedException ignore) { }
Log.p(TAG, "Failed closing LogSlurper websocket");
}
}

0 comments on commit 60b0dc5

Please sign in to comment.