From f202015b82c8d97c87532856b653a0a154782a56 Mon Sep 17 00:00:00 2001 From: nitram509 Date: Thu, 11 Oct 2018 12:36:35 +0200 Subject: [PATCH 1/3] Fix issue #8055 missing timestamps on serial monitor --- .../processing/app/AbstractTextMonitor.java | 93 +++++++++++-------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/app/src/processing/app/AbstractTextMonitor.java b/app/src/processing/app/AbstractTextMonitor.java index fdfcfba760a..09b4b3d347a 100644 --- a/app/src/processing/app/AbstractTextMonitor.java +++ b/app/src/processing/app/AbstractTextMonitor.java @@ -43,13 +43,10 @@ public abstract class AbstractTextMonitor extends AbstractMonitor { protected JComboBox lineEndings; protected JComboBox serialRates; - private SimpleDateFormat logDateFormat; - public AbstractTextMonitor(BoardPort boardPort) { super(boardPort); - logDateFormat = new SimpleDateFormat("HH:mm:ss.SSS -> "); } - + protected void onCreateWindow(Container mainPane) { Font consoleFont = Theme.getFont("console.font"); Font editorFont = PreferencesData.getFont("editor.font"); @@ -57,7 +54,7 @@ protected void onCreateWindow(Container mainPane) { mainPane.setLayout(new BorderLayout()); - textArea = new TextAreaFIFO(8000000); + textArea = new TextAreaFIFO(8_000_000); textArea.setRows(16); textArea.setColumns(40); textArea.setEditable(false); @@ -70,7 +67,7 @@ protected void onCreateWindow(Container mainPane) { scrollPane = new JScrollPane(textArea); mainPane.add(scrollPane, BorderLayout.CENTER); - + JPanel upperPane = new JPanel(); upperPane.setLayout(new BoxLayout(upperPane, BoxLayout.X_AXIS)); upperPane.setBorder(new EmptyBorder(4, 4, 4, 4)); @@ -165,7 +162,7 @@ public void onSendCommand(ActionListener listener) { textField.addActionListener(listener); sendButton.addActionListener(listener); } - + public void onClearCommand(ActionListener listener) { clearButton.addActionListener(listener); } @@ -173,41 +170,57 @@ public void onClearCommand(ActionListener listener) { public void onSerialRateChange(ActionListener listener) { serialRates.addActionListener(listener); } - - public void message(final String s) { - SwingUtilities.invokeLater(new Runnable() { - // Pre-allocate all objects used for streaming data - Date t = new Date(); - String now; - StringBuilder out = new StringBuilder(16384); - boolean isStartingLine = false; - - public void run() { - if (addTimeStampBox.isSelected()) { - t.setTime(System.currentTimeMillis()); - now = logDateFormat.format(t); - out.setLength(0); - - StringTokenizer tokenizer = new StringTokenizer(s, "\n", true); - while (tokenizer.hasMoreTokens()) { - if (isStartingLine) { - out.append(now); - } - String token = tokenizer.nextToken(); - out.append(token); - // tokenizer returns "\n" as a single token - isStartingLine = token.charAt(0) == '\n'; - } - - textArea.append(out.toString()); - } else { - textArea.append(s); - } - if (autoscrollBox.isSelected()) { - textArea.setCaretPosition(textArea.getDocument().getLength()); + public void message(final String msg) { + SwingUtilities.invokeLater(new UpdateTextAreaAction(textArea, + addTimeStampBox.isSelected(), + autoscrollBox.isSelected(), + msg)); + } + + static class UpdateTextAreaAction implements Runnable { + + private static final String LINE_SEPARATOR = "\n"; + + private String msg; + private boolean addTimeStamp; + private boolean doAutoscroll; + private TextAreaFIFO textArea; + + UpdateTextAreaAction(TextAreaFIFO textArea, boolean addTimeStamp, + boolean doAutoscroll, String msg) { + this.msg = msg; + this.textArea = textArea; + this.addTimeStamp = addTimeStamp; + this.doAutoscroll = doAutoscroll; + } + + public void run() { + if (addTimeStamp) { + textArea.append(addTimestamps(msg)); + } else { + textArea.append(msg); + } + if (doAutoscroll) { + textArea.setCaretPosition(textArea.getDocument().getLength()); + } + } + + private String addTimestamps(String text) { + String now = new SimpleDateFormat("HH:mm:ss.SSS -> ").format(new Date()); + final StringBuilder sb = new StringBuilder(text.length() + now.length()); + boolean isStartingLine = true; + StringTokenizer tokenizer = new StringTokenizer(text, LINE_SEPARATOR, true); + while (tokenizer.hasMoreTokens()) { + if (isStartingLine) { + sb.append(now); } + String token = tokenizer.nextToken(); + sb.append(token); + // tokenizer returns "\n" as a single token + isStartingLine = token.equals(LINE_SEPARATOR); } - }); + return sb.toString(); + } } } From 7676d9c0b2a6e7deae8b7135d1075021b58ce8c8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 14 Nov 2018 16:59:54 +0100 Subject: [PATCH 2/3] Replaced UpdateTextAreaAction with a lambda that performs a method call The overhead is negligible and this design simplifies a lot the class structure. More discussion here: https://github.com/arduino/Arduino/pull/8088#issuecomment-433727423 --- .../processing/app/AbstractTextMonitor.java | 67 +++++++------------ 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/app/src/processing/app/AbstractTextMonitor.java b/app/src/processing/app/AbstractTextMonitor.java index 09b4b3d347a..b6e196a16b5 100644 --- a/app/src/processing/app/AbstractTextMonitor.java +++ b/app/src/processing/app/AbstractTextMonitor.java @@ -171,56 +171,37 @@ public void onSerialRateChange(ActionListener listener) { serialRates.addActionListener(listener); } - public void message(final String msg) { - SwingUtilities.invokeLater(new UpdateTextAreaAction(textArea, - addTimeStampBox.isSelected(), - autoscrollBox.isSelected(), - msg)); + public void message(String msg) { + SwingUtilities.invokeLater(() -> updateTextArea(msg)); } - static class UpdateTextAreaAction implements Runnable { + private static final String LINE_SEPARATOR = "\n"; + private boolean isStartingLine = true; - private static final String LINE_SEPARATOR = "\n"; - - private String msg; - private boolean addTimeStamp; - private boolean doAutoscroll; - private TextAreaFIFO textArea; - - UpdateTextAreaAction(TextAreaFIFO textArea, boolean addTimeStamp, - boolean doAutoscroll, String msg) { - this.msg = msg; - this.textArea = textArea; - this.addTimeStamp = addTimeStamp; - this.doAutoscroll = doAutoscroll; + protected void updateTextArea(String msg) { + if (addTimeStampBox.isSelected()) { + textArea.append(addTimestamps(msg)); + } else { + textArea.append(msg); } - - public void run() { - if (addTimeStamp) { - textArea.append(addTimestamps(msg)); - } else { - textArea.append(msg); - } - if (doAutoscroll) { - textArea.setCaretPosition(textArea.getDocument().getLength()); - } + if (autoscrollBox.isSelected()) { + textArea.setCaretPosition(textArea.getDocument().getLength()); } + } - private String addTimestamps(String text) { - String now = new SimpleDateFormat("HH:mm:ss.SSS -> ").format(new Date()); - final StringBuilder sb = new StringBuilder(text.length() + now.length()); - boolean isStartingLine = true; - StringTokenizer tokenizer = new StringTokenizer(text, LINE_SEPARATOR, true); - while (tokenizer.hasMoreTokens()) { - if (isStartingLine) { - sb.append(now); - } - String token = tokenizer.nextToken(); - sb.append(token); - // tokenizer returns "\n" as a single token - isStartingLine = token.equals(LINE_SEPARATOR); + private String addTimestamps(String text) { + String now = new SimpleDateFormat("HH:mm:ss.SSS -> ").format(new Date()); + final StringBuilder sb = new StringBuilder(text.length() + now.length()); + StringTokenizer tokenizer = new StringTokenizer(text, LINE_SEPARATOR, true); + while (tokenizer.hasMoreTokens()) { + if (isStartingLine) { + sb.append(now); } - return sb.toString(); + String token = tokenizer.nextToken(); + sb.append(token); + // tokenizer returns "\n" as a single token + isStartingLine = token.equals(LINE_SEPARATOR); } + return sb.toString(); } } From 2d82451959f07011ac9508678589dba1fff7bd8c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 14 Nov 2018 17:05:31 +0100 Subject: [PATCH 3/3] Added tests for AbstractTextMonitor "Show Timestamp" function --- .../app/UpdateTextAreaActionTest.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app/test/processing/app/UpdateTextAreaActionTest.java diff --git a/app/test/processing/app/UpdateTextAreaActionTest.java b/app/test/processing/app/UpdateTextAreaActionTest.java new file mode 100644 index 00000000000..b32ea1850be --- /dev/null +++ b/app/test/processing/app/UpdateTextAreaActionTest.java @@ -0,0 +1,91 @@ +package processing.app; + +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +import cc.arduino.packages.BoardPort; +import processing.app.helpers.PreferencesMap; + +public class UpdateTextAreaActionTest { + + private static final String TIMESTAMP_REGEX = "\\d\\d:\\d\\d:\\d\\d.\\d\\d\\d"; + + class DummyTextMonitor extends AbstractTextMonitor { + public DummyTextMonitor(BoardPort boardPort) { + super(boardPort); + } + } + + @Before + public void setup() { + PreferencesData.defaults = new PreferencesMap(); + PreferencesData.set("editor.font", "Monospaced,plain,12"); + PreferencesData.set("gui.scale", "100"); + Theme.defaults = new PreferencesMap(); + Theme.table.put("console.font", "Monospaced,plain,12"); + } + + @Test + public void noTimestampAdded() { + DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort()); + textMon.addTimeStampBox.setSelected(false); + + textMon.updateTextArea("line1\nline2\r\nline3"); + assertThat(textMon.textArea.getText()).matches("line1\nline2\r\nline3"); + } + + @Test + public void all3LinesHaveTimestampAdded() { + DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort()); + textMon.addTimeStampBox.setSelected(true); + + textMon.updateTextArea("line1\nline2\r\nline3"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> line1\\n" + // + TIMESTAMP_REGEX + " -> line2\\r\\n" + // + TIMESTAMP_REGEX + " -> line3"); + } + + @Test + public void emptyLinesHaveTimestampToo() { + DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort()); + textMon.addTimeStampBox.setSelected(true); + + textMon.updateTextArea("line_1\n\nline_2"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> line_1\\n" + // + TIMESTAMP_REGEX + " -> \\n" + // + TIMESTAMP_REGEX + " -> line_2"); + } + + @Test + public void newLinesAreRememberedWhenNewBufferIsUsed() { + DummyTextMonitor textMon = new DummyTextMonitor(new BoardPort()); + textMon.addTimeStampBox.setSelected(true); + + textMon.updateTextArea("no newline"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> no newline"); + + textMon.updateTextArea(" more text"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> no newline more text"); + + textMon.updateTextArea("\n"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> no newline more text\n"); + + textMon.updateTextArea("\n"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> no newline more text\n" + // + TIMESTAMP_REGEX + " -> \n"); + + textMon.updateTextArea("third line"); + assertThat(textMon.textArea.getText()) + .matches(TIMESTAMP_REGEX + " -> no newline more text\n" + // + TIMESTAMP_REGEX + " -> \n" + // + TIMESTAMP_REGEX + " -> third line"); + } +} \ No newline at end of file