forked from faucetsdn/udmi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Add utility for GSheets logger to stream output to Sheets (faucetsdn#…
1 parent
8792d14
commit badb23d
Showing
12 changed files
with
651 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -92,6 +92,11 @@ | |
"description": "Source repository where the UDMI site model is stored", | ||
"type": "string", | ||
"examples": ["https://github.com/faucetsdn/udmi_site_model", "[email protected]:faucetsdn/udmi_site_model.git"] | ||
}, | ||
"sheet": { | ||
"description": "Link to a spreadsheet to observe real-time output from any tool", | ||
"type": "string", | ||
"examples": ["https://docs.google.com/spreadsheets/d/<spreadsheet_id>"] | ||
} | ||
} | ||
}, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
validator/src/main/java/com/google/udmi/util/DualOutputStream.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.google.udmi.util; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
/** | ||
* An OutputStream that duplicates output to two output streams, useful for scenarios such as | ||
* logging to two destinations simultaneously. | ||
*/ | ||
public class DualOutputStream extends OutputStream { | ||
|
||
private final OutputStream primary; | ||
private final OutputStream secondary; | ||
|
||
public DualOutputStream(OutputStream primary, OutputStream secondary) { | ||
this.primary = primary; | ||
this.secondary = secondary; | ||
} | ||
|
||
@Override | ||
public void write(int i) throws IOException { | ||
primary.write(i); | ||
secondary.write(i); | ||
} | ||
|
||
@Override | ||
public void write(byte @NotNull [] b) throws IOException { | ||
primary.write(b); | ||
secondary.write(b); | ||
} | ||
|
||
@Override | ||
public void write(byte @NotNull [] b, int off, int len) throws IOException { | ||
primary.write(b, off, len); | ||
secondary.write(b, off, len); | ||
} | ||
|
||
@Override | ||
public void flush() throws IOException { | ||
primary.flush(); | ||
secondary.flush(); | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
primary.close(); | ||
secondary.close(); | ||
} | ||
} |
256 changes: 256 additions & 0 deletions
256
validator/src/main/java/com/google/udmi/util/SheetsOutputStream.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
package com.google.udmi.util; | ||
|
||
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; | ||
import com.google.api.client.http.javanet.NetHttpTransport; | ||
import com.google.api.client.json.JsonFactory; | ||
import com.google.api.client.json.gson.GsonFactory; | ||
import com.google.api.services.sheets.v4.Sheets; | ||
import com.google.api.services.sheets.v4.SheetsScopes; | ||
import com.google.api.services.sheets.v4.model.AddSheetRequest; | ||
import com.google.api.services.sheets.v4.model.BatchUpdateSpreadsheetRequest; | ||
import com.google.api.services.sheets.v4.model.Request; | ||
import com.google.api.services.sheets.v4.model.SheetProperties; | ||
import com.google.api.services.sheets.v4.model.Spreadsheet; | ||
import com.google.api.services.sheets.v4.model.ValueRange; | ||
import com.google.auth.http.HttpCredentialsAdapter; | ||
import com.google.auth.oauth2.GoogleCredentials; | ||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.io.OutputStream; | ||
import java.io.PrintStream; | ||
import java.security.GeneralSecurityException; | ||
import java.time.Instant; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
||
/** | ||
* Generic utility to log messages to Google Sheets. This class extends {@link OutputStream} and | ||
* redirects output to a specified sheet in a Google Spreadsheet. | ||
*/ | ||
public class SheetsOutputStream extends OutputStream { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(SheetsOutputStream.class); | ||
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); | ||
static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS); | ||
private static final long DEFAULT_SYNC_TIME = 2000; | ||
private static final NetHttpTransport HTTP_TRANSPORT; | ||
|
||
static { | ||
try { | ||
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); | ||
} catch (GeneralSecurityException | IOException e) { | ||
throw new ExceptionInInitializerError("Error initializing HTTP Transport: " + e.getMessage()); | ||
} | ||
} | ||
|
||
final long syncTime; | ||
private long lastWriteMillis = 0; | ||
private final String applicationName; | ||
private final String spreadsheetId; | ||
private final String outputSheetTitle; | ||
private final Sheets sheetsService; | ||
final StringBuilder buffer = new StringBuilder(); | ||
private PrintStream originalSystemOut; | ||
private PrintStream originalSystemErr; | ||
|
||
/** | ||
* Constructs a new `GSheetsOutputStream` with default sync time. | ||
* | ||
* @param applicationName The name of the application using the Google Sheets API. | ||
* @param spreadsheetId The ID of the Google Spreadsheet. | ||
* @param outputSheetTitle The title of the sheet where the output will be written. | ||
* @throws IOException If there's an error creating the Google Sheets service or adding the sheet. | ||
*/ | ||
public SheetsOutputStream(String applicationName, String spreadsheetId, String outputSheetTitle) | ||
throws IOException { | ||
this(applicationName, spreadsheetId, outputSheetTitle, DEFAULT_SYNC_TIME); | ||
} | ||
|
||
/** | ||
* Constructs a new `GSheetsOutputStream`. | ||
* | ||
* @param applicationName The name of the application using the Google Sheets API. | ||
* @param spreadsheetId The ID of the Google Spreadsheet. | ||
* @param outputSheetTitle The title of the sheet where the output will be written. | ||
* @param syncTime The time in milliseconds to wait before syncing the buffer to the sheet. | ||
* @throws IOException If there's an error creating the Google Sheets service or adding the sheet. | ||
*/ | ||
public SheetsOutputStream(String applicationName, String spreadsheetId, String outputSheetTitle, | ||
long syncTime) | ||
throws IOException { | ||
this.applicationName = applicationName; | ||
this.spreadsheetId = spreadsheetId; | ||
this.outputSheetTitle = outputSheetTitle; | ||
this.sheetsService = createSheetsService(); | ||
this.syncTime = syncTime; | ||
addOutputSheet(); | ||
} | ||
|
||
@Override | ||
public void write(int i) { | ||
buffer.append((char) i); | ||
syncIfNeeded(); | ||
} | ||
|
||
@Override | ||
public void write(byte @NotNull [] b, int off, int len) { | ||
buffer.append(new String(b, off, len)); | ||
syncIfNeeded(); | ||
} | ||
|
||
private void syncIfNeeded() { | ||
long currentTimeMillis = Instant.now().toEpochMilli(); | ||
if (buffer.indexOf("\n") != -1 && currentTimeMillis - lastWriteMillis >= syncTime) { | ||
lastWriteMillis = currentTimeMillis; | ||
appendToSheet(); | ||
} | ||
} | ||
|
||
void appendToSheet() { | ||
String content = buffer.toString(); | ||
if (content.trim().isEmpty()) { | ||
buffer.setLength(0); // Clear buffer even if nothing to write | ||
return; | ||
} | ||
try { | ||
List<List<Object>> values = Arrays.stream(content.split("\\n")) | ||
.filter(line -> !line.trim().isEmpty()) // Filter out empty lines | ||
.map(line -> Collections.singletonList((Object) line)) | ||
.collect(Collectors.toList()); | ||
|
||
if (!values.isEmpty()) { | ||
ValueRange appendBody = new ValueRange().setValues(values); | ||
sheetsService.spreadsheets().values() | ||
.append(spreadsheetId, outputSheetTitle, appendBody) | ||
.setValueInputOption("RAW").execute(); | ||
} | ||
buffer.setLength(0); | ||
} catch (IOException e) { | ||
LOGGER.error("Error appending to sheet", e); | ||
} | ||
} | ||
|
||
Sheets createSheetsService() throws IOException { | ||
GoogleCredentials credential = | ||
GoogleCredentials.getApplicationDefault().createScoped(SCOPES); | ||
return new Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, new HttpCredentialsAdapter(credential)) | ||
.setApplicationName(this.applicationName) | ||
.build(); | ||
} | ||
|
||
|
||
private boolean outputSheetExists() throws IOException { | ||
Spreadsheet spreadsheet = sheetsService.spreadsheets().get(spreadsheetId).execute(); | ||
return spreadsheet.getSheets().stream() | ||
.anyMatch(sheet -> sheet.getProperties().getTitle().equalsIgnoreCase(outputSheetTitle)); | ||
} | ||
|
||
private void addOutputSheet() throws IOException { | ||
if (!outputSheetExists()) { | ||
BatchUpdateSpreadsheetRequest body = new BatchUpdateSpreadsheetRequest() | ||
.setRequests(List.of(new Request().setAddSheet(new AddSheetRequest() | ||
.setProperties(new SheetProperties().setTitle(outputSheetTitle))))); | ||
try { | ||
sheetsService.spreadsheets().batchUpdate(spreadsheetId, body).execute(); | ||
} catch (IOException e) { | ||
throw new IOException("Failed to add output sheet: " + outputSheetTitle, e); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Redirects `System.out` and `System.err` to google sheets. | ||
*/ | ||
public void startStream() { | ||
if (originalSystemOut == null) { | ||
originalSystemOut = System.out; | ||
originalSystemErr = System.err; | ||
DualOutputStream tee = new DualOutputStream(originalSystemOut, this); | ||
PrintStream printStream = new PrintStream(tee); | ||
System.setOut(printStream); | ||
System.setErr(printStream); | ||
} | ||
} | ||
|
||
/** | ||
* Restores `System.out` and `System.err` to their original streams. | ||
*/ | ||
public void stopStream() { | ||
if (originalSystemOut != null) { | ||
System.setOut(originalSystemOut); | ||
System.setErr(originalSystemErr); | ||
originalSystemOut = null; | ||
originalSystemErr = null; | ||
|
||
// flush out | ||
appendToSheet(); | ||
} | ||
} | ||
|
||
/** | ||
* Redirects standard input to Google Sheet output. | ||
*/ | ||
public void startStreamFromInput() { | ||
try ( | ||
DualOutputStream tee = new DualOutputStream(System.out, this); | ||
PrintStream printStream = new PrintStream(tee); | ||
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)) | ||
) { | ||
String line; | ||
while ((line = reader.readLine()) != null) { | ||
printStream.println(line); | ||
} | ||
} catch (IOException e) { | ||
throw new RuntimeException("Exception while reading input", e); | ||
} finally { | ||
// added to ensure last batch is always appended & to prevent loss in case of exceptions | ||
appendToSheet(); | ||
} | ||
} | ||
|
||
/** | ||
* Main method for the class. Can be used to start the logger from command line. | ||
* | ||
* @param args Command line arguments. Required: applicationName, spreadsheetId, sheetTitle. | ||
* Optional: syncTime. | ||
*/ | ||
public static void main(String[] args) { | ||
if (args.length < 3 || args.length > 4) { | ||
System.err.println( | ||
"Usage: GSheetsOutputStream <applicationName> <spreadsheetId> <sheetTitle> [<syncTime>]"); | ||
System.exit(1); | ||
} | ||
|
||
String applicationName = args[0]; | ||
String spreadsheetId = args[1]; | ||
String sheetTitle = args[2]; | ||
long syncTime = DEFAULT_SYNC_TIME; | ||
|
||
if (args.length == 4) { | ||
try { | ||
syncTime = Long.parseLong(args[3]); | ||
if (syncTime <= 0) { | ||
System.err.println("Sync time should be greater than zero"); | ||
System.exit(1); | ||
} | ||
} catch (NumberFormatException e) { | ||
System.err.println("Invalid sync time format: " + args[3]); | ||
System.exit(1); | ||
} | ||
} | ||
|
||
try (SheetsOutputStream sheetsOutputStream = | ||
new SheetsOutputStream(applicationName, spreadsheetId, sheetTitle, syncTime)) { | ||
sheetsOutputStream.startStreamFromInput(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
} |
238 changes: 238 additions & 0 deletions
238
validator/src/test/java/com/google/udmi/util/SheetsOutputStreamTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
package com.google.udmi.util; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.anyString; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.google.api.services.sheets.v4.Sheets; | ||
import com.google.api.services.sheets.v4.Sheets.Spreadsheets; | ||
import com.google.api.services.sheets.v4.Sheets.Spreadsheets.BatchUpdate; | ||
import com.google.api.services.sheets.v4.Sheets.Spreadsheets.Get; | ||
import com.google.api.services.sheets.v4.Sheets.Spreadsheets.Values; | ||
import com.google.api.services.sheets.v4.Sheets.Spreadsheets.Values.Append; | ||
import com.google.api.services.sheets.v4.model.AppendValuesResponse; | ||
import com.google.api.services.sheets.v4.model.BatchUpdateSpreadsheetRequest; | ||
import com.google.api.services.sheets.v4.model.BatchUpdateSpreadsheetResponse; | ||
import com.google.api.services.sheets.v4.model.Sheet; | ||
import com.google.api.services.sheets.v4.model.SheetProperties; | ||
import com.google.api.services.sheets.v4.model.Spreadsheet; | ||
import com.google.api.services.sheets.v4.model.ValueRange; | ||
import java.io.IOException; | ||
import java.io.PrintStream; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.ArgumentCaptor; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
/** | ||
* Unit tests for SheetsOutputStream.java | ||
*/ | ||
@ExtendWith(MockitoExtension.class) | ||
public class SheetsOutputStreamTest { | ||
|
||
private final Append mockAppend = mock(Append.class); | ||
private final AppendValuesResponse mockAppendValuesResponse = mock(AppendValuesResponse.class); | ||
private final BatchUpdate mockBatchUpdate = mock(BatchUpdate.class); | ||
private final BatchUpdateSpreadsheetResponse mockBatchUpdateResponse = mock( | ||
BatchUpdateSpreadsheetResponse.class); | ||
private final Get mockSpreadsheetsGet = mock(Get.class); | ||
private final Sheet mockSheet = new Sheet(); | ||
private final Sheets mockSheetsService = mock(Sheets.class); | ||
private final Spreadsheet mockSpreadSheet = mock(Spreadsheet.class); | ||
private final Spreadsheets mockSpreadsheets = mock(Spreadsheets.class); | ||
private final Values mockValues = mock(Values.class); | ||
|
||
private final String applicationName = "TestApp"; | ||
private final String spreadsheetId = "testSpreadsheetId"; | ||
private final String outputSheetTitle = "TestSheet"; | ||
private final long syncTime = 2000; | ||
private SheetsOutputStream sheetsOutputStream; | ||
|
||
|
||
/** | ||
* Mock interactions with the gcloud API. | ||
*/ | ||
@Before | ||
public void setup() throws IOException { | ||
mockSheet.setProperties( | ||
new SheetProperties().setTitle(outputSheetTitle) | ||
); | ||
|
||
when(mockSheetsService.spreadsheets()).thenReturn(mockSpreadsheets); | ||
when(mockSpreadsheets.get(anyString())).thenReturn(mockSpreadsheetsGet); | ||
when(mockSpreadsheetsGet.execute()).thenReturn(mockSpreadSheet); | ||
when(mockSpreadSheet.getSheets()).thenReturn(new ArrayList<Sheet>(List.of(mockSheet))); | ||
when(mockSpreadsheets.batchUpdate(anyString(), | ||
any(BatchUpdateSpreadsheetRequest.class))).thenReturn(mockBatchUpdate); | ||
when(mockBatchUpdate.execute()).thenReturn(mockBatchUpdateResponse); | ||
when(mockSpreadsheets.values()).thenReturn(mockValues); | ||
when(mockValues.append(any(), any(), any())).thenReturn(mockAppend); | ||
when(mockAppend.setValueInputOption(anyString())).thenReturn(mockAppend); | ||
when(mockAppend.execute()).thenReturn(mockAppendValuesResponse); | ||
sheetsOutputStream = new SheetsOutputStream(applicationName, spreadsheetId, outputSheetTitle, | ||
syncTime) { | ||
@Override | ||
Sheets createSheetsService() { | ||
return mockSheetsService; | ||
} | ||
}; | ||
} | ||
|
||
@Test | ||
public void testConstructorWithDefaultSyncTime() throws IOException { | ||
SheetsOutputStream stream = new SheetsOutputStream(applicationName, spreadsheetId, | ||
outputSheetTitle) { | ||
@Override | ||
Sheets createSheetsService() { | ||
return mockSheetsService; | ||
} | ||
}; | ||
assertNotNull(stream); | ||
} | ||
|
||
@Test | ||
public void testConstructorWithCustomSyncTime() { | ||
assertNotNull(sheetsOutputStream); | ||
assertEquals(syncTime, sheetsOutputStream.syncTime); | ||
} | ||
|
||
|
||
@Test | ||
public void testWriteSingleCharacter() throws IOException { | ||
sheetsOutputStream.startStream(); | ||
sheetsOutputStream.write('A'); | ||
assertEquals(sheetsOutputStream.buffer.toString(), "A"); | ||
sheetsOutputStream.stopStream(); | ||
|
||
// verify stream was appended to the sheet | ||
verify(mockValues, times(1)).append(any(), any(), any()); | ||
} | ||
|
||
|
||
@Test | ||
public void testWriteMultipleCharacters() throws IOException { | ||
sheetsOutputStream.startStream(); | ||
String testString = "Hello World!"; | ||
sheetsOutputStream.write(testString.getBytes(), 0, testString.length()); | ||
assertEquals(testString, sheetsOutputStream.buffer.toString()); | ||
sheetsOutputStream.stopStream(); | ||
|
||
// verify stream was appended to the sheet | ||
verify(mockValues, times(1)).append(any(), any(), any()); | ||
} | ||
|
||
@Test | ||
public void testWriteMultiLineString() throws IOException { | ||
sheetsOutputStream.startStream(); | ||
String testString = "First line.\nSecond line.\nThird line."; | ||
sheetsOutputStream.write(testString.getBytes(), 0, testString.length()); | ||
|
||
// verify stream was appended to the sheet | ||
ArgumentCaptor<ValueRange> argumentCaptor = ArgumentCaptor.forClass(ValueRange.class); | ||
verify(mockValues, times(1)).append(eq(spreadsheetId), eq(outputSheetTitle), | ||
argumentCaptor.capture()); | ||
ValueRange capturedValue = argumentCaptor.getValue(); | ||
assertEquals(3, capturedValue.getValues().size()); | ||
assertEquals("First line.", capturedValue.getValues().get(0).get(0)); | ||
assertEquals("Second line.", capturedValue.getValues().get(1).get(0)); | ||
assertEquals("Third line.", capturedValue.getValues().get(2).get(0)); | ||
|
||
// buffer is emptied after appending to the sheet | ||
assertEquals("", sheetsOutputStream.buffer.toString()); | ||
|
||
sheetsOutputStream.stopStream(); | ||
} | ||
|
||
@Test | ||
public void testAppendToSheetEmptyContent() throws IOException { | ||
sheetsOutputStream.buffer.append(" \n \n"); // Whitespace and empty lines | ||
sheetsOutputStream.appendToSheet(); | ||
|
||
// empty content is not appended to the sheet | ||
verify(mockValues, never()).append(any(), any(), any()); | ||
assertEquals("", sheetsOutputStream.buffer.toString()); | ||
} | ||
|
||
@Test | ||
public void testAddSheetIfNotExist() throws IOException { | ||
when(mockSpreadsheetsGet.execute()).thenReturn( | ||
new Spreadsheet().setSheets(Collections.emptyList())); | ||
SheetsOutputStream outputStream = | ||
new SheetsOutputStream(applicationName, spreadsheetId, outputSheetTitle, syncTime) { | ||
@Override | ||
Sheets createSheetsService() { | ||
return mockSheetsService; | ||
} | ||
}; | ||
verify(mockBatchUpdate, times(1)).execute(); | ||
} | ||
|
||
@Test | ||
public void testAddSheetFails() throws IOException { | ||
when(mockSpreadsheetsGet.execute()).thenReturn( | ||
new Spreadsheet().setSheets(Collections.emptyList())); | ||
when(mockBatchUpdate.execute()).thenThrow(new IOException("Failed to add sheet")); | ||
assertThrows( | ||
IOException.class, | ||
() -> new SheetsOutputStream(applicationName, spreadsheetId, outputSheetTitle, | ||
syncTime) { | ||
@Override | ||
Sheets createSheetsService() { | ||
return mockSheetsService; | ||
} | ||
}); | ||
} | ||
|
||
|
||
@Test | ||
public void testSheetExists() throws IOException { | ||
SheetProperties sheetProperties = new SheetProperties().setTitle(outputSheetTitle); | ||
Sheet sheet = new Sheet().setProperties(sheetProperties); | ||
when(mockSpreadsheetsGet.execute()).thenReturn( | ||
new Spreadsheet().setSheets(Collections.singletonList(sheet))); | ||
SheetsOutputStream outputStream = | ||
new SheetsOutputStream(applicationName, spreadsheetId, outputSheetTitle, syncTime) { | ||
@Override | ||
Sheets createSheetsService() { | ||
return mockSheetsService; | ||
} | ||
}; | ||
verify(mockBatchUpdate, never()).execute(); | ||
} | ||
|
||
|
||
@Test | ||
public void testStartAndStopStream() throws IOException { | ||
SheetsOutputStream outputStream = | ||
new SheetsOutputStream(applicationName, spreadsheetId, outputSheetTitle, syncTime) { | ||
@Override | ||
Sheets createSheetsService() { | ||
return mockSheetsService; | ||
} | ||
}; | ||
PrintStream originalOut = System.out; | ||
PrintStream originalErr = System.err; | ||
outputStream.startStream(); | ||
assertNotEquals(originalOut, System.out); | ||
assertNotEquals(originalErr, System.err); | ||
String testString = "Test output"; | ||
System.out.println(testString); | ||
outputStream.stopStream(); | ||
assertEquals(originalOut, System.out); | ||
assertEquals(originalErr, System.err); | ||
} | ||
|
||
} |