[datalog] Move all DataLog functionality to new datalog library (#7641)

Currently the major DataLog backend API (reading and writing) is split between wpiutil and glass. In the interest of allowing code that wants to use these APIs to not need to link to glass and declutter wpiutil, all of those APIs are moved to a new library named "datalog".

Signed-off-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Jade Turner <spacey-sooty@proton.me>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
This commit is contained in:
DeltaDizzy
2025-02-19 23:08:17 -06:00
committed by GitHub
parent ac1705ae2b
commit da47f06d70
99 changed files with 778 additions and 330 deletions

View File

@@ -0,0 +1,799 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.datalog;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import edu.wpi.first.util.struct.Struct;
import edu.wpi.first.util.struct.StructSerializable;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.Objects;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@SuppressWarnings("AvoidEscapedUnicodeCharacters")
class DataLogTest {
static class ImmutableThingStruct implements Struct<ImmutableThing> {
@Override
public Class<ImmutableThing> getTypeClass() {
return ImmutableThing.class;
}
@Override
public String getTypeName() {
return "Thing";
}
@Override
public int getSize() {
return 1;
}
@Override
public String getSchema() {
return "uint8 value";
}
@Override
public ImmutableThing unpack(ByteBuffer bb) {
return new ImmutableThing(bb.get());
}
@Override
public void pack(ByteBuffer bb, ImmutableThing value) {
bb.put(value.m_x);
}
@Override
public boolean isImmutable() {
return true;
}
}
static class ImmutableThing implements StructSerializable {
byte m_x;
ImmutableThing(int x) {
m_x = (byte) x;
}
@Override
public boolean equals(Object obj) {
return obj instanceof ImmutableThing other && other.m_x == m_x;
}
@Override
public int hashCode() {
return Objects.hash(m_x);
}
public static final ImmutableThingStruct struct = new ImmutableThingStruct();
}
static class CloneableThingStruct implements Struct<CloneableThing> {
@Override
public Class<CloneableThing> getTypeClass() {
return CloneableThing.class;
}
@Override
public String getTypeName() {
return "Thing";
}
@Override
public int getSize() {
return 1;
}
@Override
public String getSchema() {
return "uint8 value";
}
@Override
public CloneableThing unpack(ByteBuffer bb) {
return new CloneableThing(bb.get());
}
@Override
public void pack(ByteBuffer bb, CloneableThing value) {
bb.put(value.m_x);
}
@Override
public boolean isCloneable() {
return true;
}
@Override
public CloneableThing clone(CloneableThing obj) throws CloneNotSupportedException {
return obj.clone();
}
}
@SuppressWarnings("MemberName")
private static int cloneCalls;
static class CloneableThing implements StructSerializable, Cloneable {
byte m_x;
CloneableThing(int x) {
m_x = (byte) x;
}
@Override
public boolean equals(Object obj) {
return obj instanceof CloneableThing other && other.m_x == m_x;
}
@Override
public int hashCode() {
return Objects.hash(m_x);
}
@Override
public CloneableThing clone() throws CloneNotSupportedException {
CloneableThing thing = (CloneableThing) super.clone();
cloneCalls++;
return thing;
}
public static final CloneableThingStruct struct = new CloneableThingStruct();
}
static class ThingStruct implements Struct<Thing> {
@Override
public Class<Thing> getTypeClass() {
return Thing.class;
}
@Override
public String getTypeName() {
return "Thing";
}
@Override
public int getSize() {
return 1;
}
@Override
public String getSchema() {
return "uint8 value";
}
@Override
public Thing unpack(ByteBuffer bb) {
return new Thing(bb.get());
}
@Override
public void pack(ByteBuffer bb, Thing value) {
bb.put(value.m_x);
}
}
static class Thing implements StructSerializable {
byte m_x;
Thing(int x) {
m_x = (byte) x;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Thing other && other.m_x == m_x;
}
@Override
public int hashCode() {
return Objects.hash(m_x);
}
public static final ThingStruct struct = new ThingStruct();
}
@SuppressWarnings("MemberName")
private ByteArrayOutputStream data;
@SuppressWarnings("MemberName")
private DataLog log;
@BeforeEach
public void init() {
data = new ByteArrayOutputStream();
log = new DataLogWriter(data);
cloneCalls = 0;
}
@AfterEach
public void shutdown() {
log.close();
}
@Test
void testSimpleInt() {
int entry = log.start("test", "int64", "", 1);
log.appendInteger(entry, 1, 2);
log.flush();
assertEquals(54, data.size());
}
@Test
void testBooleanAppend() {
BooleanLogEntry entry = new BooleanLogEntry(log, "a", 5);
entry.append(false, 7);
log.flush();
assertEquals(46, data.size());
}
@Test
void testBooleanUpdate() {
BooleanLogEntry entry = new BooleanLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(false, 7);
log.flush();
assertEquals(46, data.size());
assertTrue(entry.hasLastValue());
assertFalse(entry.getLastValue());
entry.update(false, 8);
log.flush();
assertEquals(46, data.size());
entry.update(true, 9);
log.flush();
assertEquals(51, data.size());
assertTrue(entry.hasLastValue());
assertTrue(entry.getLastValue());
}
@Test
void testIntegerAppend() {
IntegerLogEntry entry = new IntegerLogEntry(log, "a", 5);
entry.append(5, 7);
log.flush();
assertEquals(51, data.size());
}
@Test
void testIntegerUpdate() {
IntegerLogEntry entry = new IntegerLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(0, 7);
log.flush();
assertEquals(51, data.size());
assertTrue(entry.hasLastValue());
assertEquals(0, entry.getLastValue());
entry.update(0, 8);
log.flush();
assertEquals(51, data.size());
entry.update(2, 9);
log.flush();
assertEquals(63, data.size());
assertTrue(entry.hasLastValue());
assertEquals(2, entry.getLastValue());
}
@Test
void testFloatAppend() {
FloatLogEntry entry = new FloatLogEntry(log, "a", 5);
entry.append(5.0f, 7);
log.flush();
assertEquals(47, data.size());
}
@Test
void testFloatUpdate() {
FloatLogEntry entry = new FloatLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(0.0f, 7);
log.flush();
assertEquals(47, data.size());
assertTrue(entry.hasLastValue());
assertEquals(0.0f, entry.getLastValue());
entry.update(0.0f, 8);
log.flush();
assertEquals(47, data.size());
entry.update(0.1f, 9);
log.flush();
assertEquals(55, data.size());
assertTrue(entry.hasLastValue());
assertEquals(0.1f, entry.getLastValue());
}
@Test
void testDoubleAppend() {
DoubleLogEntry entry = new DoubleLogEntry(log, "a", 5);
entry.append(5.0, 7);
log.flush();
assertEquals(52, data.size());
}
@Test
void testDoubleUpdate() {
DoubleLogEntry entry = new DoubleLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(0.0, 7);
log.flush();
assertEquals(52, data.size());
assertTrue(entry.hasLastValue());
assertEquals(0.0, entry.getLastValue());
entry.update(0.0, 8);
log.flush();
assertEquals(52, data.size());
entry.update(0.1, 9);
log.flush();
assertEquals(64, data.size());
assertTrue(entry.hasLastValue());
assertEquals(0.1, entry.getLastValue());
}
@Test
void testStringAppend() {
StringLogEntry entry = new StringLogEntry(log, "a", 5);
entry.append("x", 7);
log.flush();
assertEquals(45, data.size());
}
@Test
void testStringUpdate() {
StringLogEntry entry = new StringLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update("x", 7);
log.flush();
assertEquals(45, data.size());
assertTrue(entry.hasLastValue());
assertEquals("x", entry.getLastValue());
entry.update("x", 8);
log.flush();
assertEquals(45, data.size());
entry.update("y", 9);
log.flush();
assertEquals(50, data.size());
assertTrue(entry.hasLastValue());
assertEquals("y", entry.getLastValue());
entry.update("yy", 10);
log.flush();
assertEquals(56, data.size());
assertTrue(entry.hasLastValue());
assertEquals("yy", entry.getLastValue());
entry.update("", 11);
log.flush();
assertEquals(60, data.size());
assertTrue(entry.hasLastValue());
assertEquals("", entry.getLastValue());
}
@Test
void testRawAppend() {
RawLogEntry entry = new RawLogEntry(log, "a", 5);
entry.append(new byte[] {5}, 7);
log.flush();
assertEquals(42, data.size());
}
@Test
void testRawUpdate() {
RawLogEntry entry = new RawLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(new byte[] {5}, 7);
log.flush();
assertEquals(42, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new byte[] {5}, entry.getLastValue());
entry.update(new byte[] {5}, 8);
log.flush();
assertEquals(42, data.size());
entry.update(new byte[] {6}, 9);
log.flush();
assertEquals(47, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new byte[] {6}, entry.getLastValue());
entry.update(new byte[] {6, 6}, 10);
log.flush();
assertEquals(53, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new byte[] {6, 6}, entry.getLastValue());
entry.update(new byte[] {}, 11);
log.flush();
assertEquals(57, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new byte[] {}, entry.getLastValue());
}
@Test
void testBooleanArrayAppendEmpty() {
BooleanArrayLogEntry entry = new BooleanArrayLogEntry(log, "a", 5);
entry.append(new boolean[] {}, 7);
log.flush();
assertEquals(47, data.size());
}
@Test
void testBooleanArrayAppend() {
BooleanArrayLogEntry entry = new BooleanArrayLogEntry(log, "a", 5);
entry.append(new boolean[] {false}, 7);
log.flush();
assertEquals(48, data.size());
}
@Test
void testBooleanArrayUpdate() {
BooleanArrayLogEntry entry = new BooleanArrayLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(new boolean[] {false}, 7);
log.flush();
assertEquals(48, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new boolean[] {false}, entry.getLastValue());
entry.update(new boolean[] {false}, 8);
log.flush();
assertEquals(48, data.size());
entry.update(new boolean[] {true}, 9);
log.flush();
assertEquals(53, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new boolean[] {true}, entry.getLastValue());
entry.update(new boolean[] {}, 10);
log.flush();
assertEquals(57, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new boolean[] {}, entry.getLastValue());
}
@Test
void testIntegerArrayAppendEmpty() {
IntegerArrayLogEntry entry = new IntegerArrayLogEntry(log, "a", 5);
entry.append(new long[] {}, 7);
log.flush();
assertEquals(45, data.size());
}
@Test
void testIntegerArrayAppend() {
IntegerArrayLogEntry entry = new IntegerArrayLogEntry(log, "a", 5);
entry.append(new long[] {1}, 7);
log.flush();
assertEquals(53, data.size());
}
@Test
void testIntegerArrayUpdate() {
IntegerArrayLogEntry entry = new IntegerArrayLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(new long[] {1}, 7);
log.flush();
assertEquals(53, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new long[] {1}, entry.getLastValue());
entry.update(new long[] {1}, 8);
log.flush();
assertEquals(53, data.size());
entry.update(new long[] {2}, 9);
log.flush();
assertEquals(65, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new long[] {2}, entry.getLastValue());
entry.update(new long[] {}, 10);
log.flush();
assertEquals(69, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new long[] {}, entry.getLastValue());
}
@Test
void testDoubleArrayAppendEmpty() {
DoubleArrayLogEntry entry = new DoubleArrayLogEntry(log, "a", 5);
entry.append(new double[] {}, 7);
log.flush();
assertEquals(46, data.size());
}
@Test
void testDoubleArrayAppend() {
DoubleArrayLogEntry entry = new DoubleArrayLogEntry(log, "a", 5);
entry.append(new double[] {1.0}, 7);
log.flush();
assertEquals(54, data.size());
}
@Test
void testDoubleArrayUpdate() {
DoubleArrayLogEntry entry = new DoubleArrayLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(new double[] {1.0}, 7);
log.flush();
assertEquals(54, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new double[] {1.0}, entry.getLastValue());
entry.update(new double[] {1.0}, 8);
log.flush();
assertEquals(54, data.size());
entry.update(new double[] {2.0}, 9);
log.flush();
assertEquals(66, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new double[] {2}, entry.getLastValue());
entry.update(new double[] {}, 10);
log.flush();
assertEquals(70, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new double[] {}, entry.getLastValue());
}
@Test
void testFloatArrayAppendEmpty() {
FloatArrayLogEntry entry = new FloatArrayLogEntry(log, "a", 5);
entry.append(new float[] {}, 7);
log.flush();
assertEquals(45, data.size());
}
@Test
void testFloatArrayAppend() {
FloatArrayLogEntry entry = new FloatArrayLogEntry(log, "a", 5);
entry.append(new float[] {1.0f}, 7);
log.flush();
assertEquals(49, data.size());
}
@Test
void testFloatArrayUpdate() {
FloatArrayLogEntry entry = new FloatArrayLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(new float[] {1.0f}, 7);
log.flush();
assertEquals(49, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new float[] {1.0f}, entry.getLastValue());
entry.update(new float[] {1.0f}, 8);
log.flush();
assertEquals(49, data.size());
entry.update(new float[] {2.0f}, 9);
log.flush();
assertEquals(57, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new float[] {2.0f}, entry.getLastValue());
entry.update(new float[] {}, 10);
log.flush();
assertEquals(61, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new float[] {}, entry.getLastValue());
}
@Test
void testStringArrayAppendEmpty() {
StringArrayLogEntry entry = new StringArrayLogEntry(log, "a", 5);
entry.append(new String[] {}, 7);
entry.append(new String[] {}, 7);
log.flush();
assertEquals(58, data.size());
}
@Test
void testStringArrayAppend() {
StringArrayLogEntry entry = new StringArrayLogEntry(log, "a", 5);
entry.append(new String[] {"x"}, 7);
log.flush();
assertEquals(55, data.size());
}
@Test
void testStringArrayUpdate() {
StringArrayLogEntry entry = new StringArrayLogEntry(log, "a", 5);
assertFalse(entry.hasLastValue());
entry.update(new String[] {"x"}, 7);
log.flush();
assertEquals(55, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new String[] {"x"}, entry.getLastValue());
entry.update(new String[] {"x"}, 8);
log.flush();
assertEquals(55, data.size());
entry.update(new String[] {"y"}, 9);
log.flush();
assertEquals(68, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new String[] {"y"}, entry.getLastValue());
entry.update(new String[] {}, 10);
log.flush();
assertEquals(76, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new String[] {}, entry.getLastValue());
}
@Test
void testStruct() {
StructLogEntry<Thing> entry = StructLogEntry.create(log, "a", Thing.struct, 5);
entry.append(new Thing((byte) 1), 6);
entry.append(new Thing((byte) 0), 7);
}
@Test
void testStructUpdate() {
StructLogEntry<Thing> entry = StructLogEntry.create(log, "a", Thing.struct, 5);
assertFalse(entry.hasLastValue());
entry.update(new Thing(1), 7);
log.flush();
assertEquals(120, data.size());
assertTrue(entry.hasLastValue());
assertEquals(new Thing(1), entry.getLastValue());
entry.update(new Thing(1), 8);
log.flush();
assertEquals(120, data.size());
entry.update(new Thing(2), 9);
log.flush();
assertEquals(125, data.size());
assertTrue(entry.hasLastValue());
assertEquals(new Thing(2), entry.getLastValue());
}
@Test
void testCloneableStructUpdate() {
StructLogEntry<CloneableThing> entry =
StructLogEntry.create(log, "a", CloneableThing.struct, 5);
assertFalse(entry.hasLastValue());
entry.update(new CloneableThing(1), 7);
assertEquals(1, cloneCalls);
log.flush();
assertEquals(120, data.size());
assertTrue(entry.hasLastValue());
assertEquals(new CloneableThing(1), entry.getLastValue());
assertEquals(2, cloneCalls);
entry.update(new CloneableThing(1), 8);
assertEquals(2, cloneCalls);
log.flush();
assertEquals(120, data.size());
entry.update(new CloneableThing(2), 9);
assertEquals(3, cloneCalls);
log.flush();
assertEquals(125, data.size());
assertTrue(entry.hasLastValue());
assertEquals(new CloneableThing(2), entry.getLastValue());
assertEquals(4, cloneCalls);
}
@Test
void testImmutableStructUpdate() {
StructLogEntry<ImmutableThing> entry =
StructLogEntry.create(log, "a", ImmutableThing.struct, 5);
assertFalse(entry.hasLastValue());
entry.update(new ImmutableThing(1), 7);
log.flush();
assertEquals(120, data.size());
assertTrue(entry.hasLastValue());
assertEquals(new ImmutableThing(1), entry.getLastValue());
entry.update(new ImmutableThing(1), 8);
log.flush();
assertEquals(120, data.size());
entry.update(new ImmutableThing(2), 9);
log.flush();
assertEquals(125, data.size());
assertTrue(entry.hasLastValue());
assertEquals(new ImmutableThing(2), entry.getLastValue());
}
@Test
void testStructArrayUpdate() {
StructArrayLogEntry<Thing> entry = StructArrayLogEntry.create(log, "a", Thing.struct, 5);
assertFalse(entry.hasLastValue());
entry.update(new Thing[] {new Thing(1), new Thing(2)}, 7);
log.flush();
assertEquals(123, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new Thing[] {new Thing(1), new Thing(2)}, entry.getLastValue());
entry.update(new Thing[] {new Thing(1), new Thing(2)}, 8);
log.flush();
assertEquals(123, data.size());
entry.update(new Thing[] {new Thing(1), new Thing(3)}, 9);
log.flush();
assertEquals(129, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new Thing[] {new Thing(1), new Thing(3)}, entry.getLastValue());
entry.update(new Thing[] {}, 10);
log.flush();
assertEquals(133, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new Thing[] {}, entry.getLastValue());
}
@Test
void testCloneableStructArrayUpdate() {
StructArrayLogEntry<CloneableThing> entry =
StructArrayLogEntry.create(log, "a", CloneableThing.struct, 5);
assertFalse(entry.hasLastValue());
entry.update(new CloneableThing[] {new CloneableThing(1), new CloneableThing(2)}, 7);
assertEquals(2, cloneCalls);
log.flush();
assertEquals(123, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(
new CloneableThing[] {new CloneableThing(1), new CloneableThing(2)}, entry.getLastValue());
assertEquals(4, cloneCalls);
entry.update(new CloneableThing[] {new CloneableThing(1), new CloneableThing(2)}, 8);
assertEquals(4, cloneCalls);
log.flush();
assertEquals(123, data.size());
entry.update(new CloneableThing[] {new CloneableThing(1), new CloneableThing(3)}, 9);
assertEquals(6, cloneCalls);
log.flush();
assertEquals(129, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(
new CloneableThing[] {new CloneableThing(1), new CloneableThing(3)}, entry.getLastValue());
assertEquals(8, cloneCalls);
entry.update(new CloneableThing[] {}, 10);
assertEquals(8, cloneCalls);
log.flush();
assertEquals(133, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new CloneableThing[] {}, entry.getLastValue());
assertEquals(8, cloneCalls);
}
@Test
void testImmutableStructArrayUpdate() {
StructArrayLogEntry<ImmutableThing> entry =
StructArrayLogEntry.create(log, "a", ImmutableThing.struct, 5);
assertFalse(entry.hasLastValue());
entry.update(new ImmutableThing[] {new ImmutableThing(1), new ImmutableThing(2)}, 7);
log.flush();
assertEquals(123, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(
new ImmutableThing[] {new ImmutableThing(1), new ImmutableThing(2)}, entry.getLastValue());
entry.update(new ImmutableThing[] {new ImmutableThing(1), new ImmutableThing(2)}, 8);
log.flush();
assertEquals(123, data.size());
entry.update(new ImmutableThing[] {new ImmutableThing(1), new ImmutableThing(3)}, 9);
log.flush();
assertEquals(129, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(
new ImmutableThing[] {new ImmutableThing(1), new ImmutableThing(3)}, entry.getLastValue());
entry.update(new ImmutableThing[] {}, 10);
log.flush();
assertEquals(133, data.size());
assertTrue(entry.hasLastValue());
assertArrayEquals(new ImmutableThing[] {}, entry.getLastValue());
}
}

View File

@@ -0,0 +1,674 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <array>
#include <memory>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include <wpi/Logger.h>
#include <wpi/raw_ostream.h>
#include "wpi/datalog/DataLogWriter.h"
namespace {
struct ThingA {
int x = 0;
};
inline bool operator==(const ThingA& a, const ThingA& b) {
return a.x == b.x;
}
struct ThingB {
int x = 0;
};
struct ThingC {
int x = 0;
};
struct Info1 {
int info = 0;
};
struct Info2 {
int info = 0;
};
} // namespace
template <>
struct wpi::Struct<ThingA> {
static constexpr std::string_view GetTypeName() { return "ThingA"; }
static constexpr size_t GetSize() { return 1; }
static constexpr std::string_view GetSchema() { return "uint8 value"; }
static ThingA Unpack(std::span<const uint8_t> data) {
return ThingA{.x = data[0]};
}
static void Pack(std::span<uint8_t> data, const ThingA& value) {
data[0] = value.x;
}
};
template <>
struct wpi::Struct<ThingB, Info1> {
static constexpr std::string_view GetTypeName(const Info1&) {
return "ThingB";
}
static constexpr size_t GetSize(const Info1&) { return 1; }
static constexpr std::string_view GetSchema(const Info1&) {
return "uint8 value";
}
static ThingB Unpack(std::span<const uint8_t> data, const Info1&) {
return ThingB{.x = data[0]};
}
static void Pack(std::span<uint8_t> data, const ThingB& value, const Info1&) {
data[0] = value.x;
}
};
template <>
struct wpi::Struct<ThingC> {
static constexpr std::string_view GetTypeName() { return "ThingC"; }
static constexpr size_t GetSize() { return 1; }
static constexpr std::string_view GetSchema() { return "uint8 value"; }
static ThingC Unpack(std::span<const uint8_t> data) {
return ThingC{.x = data[0]};
}
static void Pack(std::span<uint8_t> data, const ThingC& value) {
data[0] = value.x;
}
};
template <>
struct wpi::Struct<ThingC, Info1> {
static constexpr std::string_view GetTypeName(const Info1&) {
return "ThingC";
}
static constexpr size_t GetSize(const Info1&) { return 1; }
static constexpr std::string_view GetSchema(const Info1&) {
return "uint8 value";
}
static ThingC Unpack(std::span<const uint8_t> data, const Info1&) {
return ThingC{.x = data[0]};
}
static void Pack(std::span<uint8_t> data, const ThingC& value, const Info1&) {
data[0] = value.x;
}
};
template <>
struct wpi::Struct<ThingC, Info2> {
static constexpr std::string_view GetTypeName(const Info2&) {
return "ThingC";
}
static constexpr size_t GetSize(const Info2&) { return 1; }
static constexpr std::string_view GetSchema(const Info2&) {
return "uint8 value";
}
static ThingC Unpack(std::span<const uint8_t> data, const Info2&) {
return ThingC{.x = data[0]};
}
static void Pack(std::span<uint8_t> data, const ThingC& value, const Info2&) {
data[0] = value.x;
}
};
static_assert(wpi::StructSerializable<ThingA>);
static_assert(!wpi::StructSerializable<ThingA, Info1>);
static_assert(!wpi::StructSerializable<ThingB>);
static_assert(wpi::StructSerializable<ThingB, Info1>);
static_assert(!wpi::StructSerializable<ThingB, Info2>);
static_assert(wpi::StructSerializable<ThingC>);
static_assert(wpi::StructSerializable<ThingC, Info1>);
static_assert(wpi::StructSerializable<ThingC, Info2>);
class DataLogTest : public ::testing::Test {
public:
wpi::Logger msglog;
std::vector<uint8_t> data;
wpi::log::DataLogWriter log{msglog,
std::make_unique<wpi::raw_uvector_ostream>(data)};
};
TEST_F(DataLogTest, SimpleInt) {
int entry = log.Start("test", "int64", "", 1);
log.AppendInteger(entry, 1, 2);
log.Flush();
ASSERT_EQ(data.size(), 54u);
}
TEST_F(DataLogTest, BooleanAppend) {
wpi::log::BooleanLogEntry entry{log, "a", 5};
entry.Append(false, 7);
log.Flush();
ASSERT_EQ(data.size(), 46u);
}
TEST_F(DataLogTest, BooleanUpdate) {
wpi::log::BooleanLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(false, 7);
log.Flush();
ASSERT_EQ(data.size(), 46u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), false);
entry.Update(false, 8);
log.Flush();
ASSERT_EQ(data.size(), 46u);
entry.Update(true, 9);
log.Flush();
ASSERT_EQ(data.size(), 51u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), true);
}
TEST_F(DataLogTest, IntegerAppend) {
wpi::log::IntegerLogEntry entry{log, "a", 5};
entry.Append(5, 7);
log.Flush();
ASSERT_EQ(data.size(), 51u);
}
TEST_F(DataLogTest, IntegerUpdate) {
wpi::log::IntegerLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(0, 7);
log.Flush();
ASSERT_EQ(data.size(), 51u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0);
entry.Update(0, 8);
log.Flush();
ASSERT_EQ(data.size(), 51u);
entry.Update(2, 9);
log.Flush();
ASSERT_EQ(data.size(), 63u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 2);
}
TEST_F(DataLogTest, FloatAppend) {
wpi::log::FloatLogEntry entry{log, "a", 5};
entry.Append(5.0, 7);
log.Flush();
ASSERT_EQ(data.size(), 47u);
}
TEST_F(DataLogTest, FloatUpdate) {
wpi::log::FloatLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(0.0f, 7);
log.Flush();
ASSERT_EQ(data.size(), 47u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.0f);
entry.Update(0.0f, 8);
log.Flush();
ASSERT_EQ(data.size(), 47u);
entry.Update(0.1f, 9);
log.Flush();
ASSERT_EQ(data.size(), 55u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.1f);
}
TEST_F(DataLogTest, DoubleAppend) {
wpi::log::DoubleLogEntry entry{log, "a", 5};
entry.Append(5.0, 7);
log.Flush();
ASSERT_EQ(data.size(), 52u);
}
TEST_F(DataLogTest, DoubleUpdate) {
wpi::log::DoubleLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(0.0, 7);
log.Flush();
ASSERT_EQ(data.size(), 52u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.0);
entry.Update(0.0, 8);
log.Flush();
ASSERT_EQ(data.size(), 52u);
entry.Update(0.1, 9);
log.Flush();
ASSERT_EQ(data.size(), 64u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), 0.1);
}
TEST_F(DataLogTest, StringAppend) {
wpi::log::StringLogEntry entry{log, "a", 5};
entry.Append("x", 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
}
TEST_F(DataLogTest, StringUpdate) {
wpi::log::StringLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.HasLastValue());
entry.Update("x", 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "x");
entry.Update("x", 8);
log.Flush();
ASSERT_EQ(data.size(), 45u);
entry.Update("y", 9);
log.Flush();
ASSERT_EQ(data.size(), 50u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "y");
entry.Update("yy", 10);
log.Flush();
ASSERT_EQ(data.size(), 56u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "yy");
entry.Update("", 11);
log.Flush();
ASSERT_EQ(data.size(), 60u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), "");
}
TEST_F(DataLogTest, RawAppend) {
wpi::log::RawLogEntry entry{log, "a", 5};
entry.Append({{5}}, 7);
log.Flush();
ASSERT_EQ(data.size(), 42u);
}
TEST_F(DataLogTest, RawUpdate) {
wpi::log::RawLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.HasLastValue());
entry.Update({{5}}, 7);
log.Flush();
ASSERT_EQ(data.size(), 42u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<uint8_t>{5});
entry.Update({{5}}, 8);
log.Flush();
ASSERT_EQ(data.size(), 42u);
entry.Update({{6}}, 9);
log.Flush();
ASSERT_EQ(data.size(), 47u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<uint8_t>{6});
entry.Update({{6, 6}}, 9);
log.Flush();
ASSERT_EQ(data.size(), 53u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), (std::vector<uint8_t>{6, 6}));
entry.Update(std::span<const uint8_t>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 57u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<uint8_t>{});
}
TEST_F(DataLogTest, BooleanArrayAppendEmpty) {
wpi::log::BooleanArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const bool>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 47u);
}
TEST_F(DataLogTest, BooleanArrayAppend) {
wpi::log::BooleanArrayLogEntry entry{log, "a", 5};
entry.Append({false}, 7);
log.Flush();
ASSERT_EQ(data.size(), 48u);
}
TEST_F(DataLogTest, BooleanArrayUpdate) {
wpi::log::BooleanArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({false}, 7);
log.Flush();
ASSERT_EQ(data.size(), 48u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int>{false});
entry.Update({false}, 8);
log.Flush();
ASSERT_EQ(data.size(), 48u);
entry.Update({true}, 9);
log.Flush();
ASSERT_EQ(data.size(), 53u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int>{true});
entry.Update(std::span<const bool>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 57u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int>{});
}
TEST_F(DataLogTest, IntegerArrayAppendEmpty) {
wpi::log::IntegerArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const int64_t>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
}
TEST_F(DataLogTest, IntegerArrayAppend) {
wpi::log::IntegerArrayLogEntry entry{log, "a", 5};
entry.Append({1}, 7);
log.Flush();
ASSERT_EQ(data.size(), 53u);
}
TEST_F(DataLogTest, IntegerArrayUpdate) {
wpi::log::IntegerArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({1}, 7);
log.Flush();
ASSERT_EQ(data.size(), 53u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int64_t>{1});
entry.Update({1}, 8);
log.Flush();
ASSERT_EQ(data.size(), 53u);
entry.Update({2}, 9);
log.Flush();
ASSERT_EQ(data.size(), 65u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int64_t>{2});
entry.Update(std::span<const int64_t>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 69u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<int64_t>{});
}
TEST_F(DataLogTest, DoubleArrayAppendEmpty) {
wpi::log::DoubleArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const double>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 46u);
}
TEST_F(DataLogTest, DoubleArrayAppend) {
wpi::log::DoubleArrayLogEntry entry{log, "a", 5};
entry.Append({1.0}, 7);
log.Flush();
ASSERT_EQ(data.size(), 54u);
}
TEST_F(DataLogTest, DoubleArrayUpdate) {
wpi::log::DoubleArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({1.0}, 7);
log.Flush();
ASSERT_EQ(data.size(), 54u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<double>{1.0});
entry.Update({1.0}, 8);
log.Flush();
ASSERT_EQ(data.size(), 54u);
entry.Update({2.0}, 9);
log.Flush();
ASSERT_EQ(data.size(), 66u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<double>{2});
entry.Update(std::span<const double>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 70u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<double>{});
}
TEST_F(DataLogTest, FloatArrayAppendEmpty) {
wpi::log::FloatArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const float>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 45u);
}
TEST_F(DataLogTest, FloatArrayAppend) {
wpi::log::FloatArrayLogEntry entry{log, "a", 5};
entry.Append({1.0f}, 7);
log.Flush();
ASSERT_EQ(data.size(), 49u);
}
TEST_F(DataLogTest, FloatArrayUpdate) {
wpi::log::FloatArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({1.0f}, 7);
log.Flush();
ASSERT_EQ(data.size(), 49u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<float>{1.0f});
entry.Update({1.0f}, 8);
log.Flush();
ASSERT_EQ(data.size(), 49u);
entry.Update({2.0f}, 9);
log.Flush();
ASSERT_EQ(data.size(), 57u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<float>{2.0f});
entry.Update(std::span<const float>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 61u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<float>{});
}
TEST_F(DataLogTest, StringArrayAppendEmpty) {
wpi::log::StringArrayLogEntry entry{log, "a", 5};
entry.Append(std::span<const std::string>{}, 7);
entry.Append(std::span<const std::string_view>{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 58u);
}
TEST_F(DataLogTest, StringArrayAppend) {
wpi::log::StringArrayLogEntry entry{log, "a", 5};
entry.Append({"x"}, 7);
log.Flush();
ASSERT_EQ(data.size(), 55u);
}
TEST_F(DataLogTest, StringArrayUpdate) {
wpi::log::StringArrayLogEntry entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({"x"}, 7);
log.Flush();
ASSERT_EQ(data.size(), 55u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<std::string>{"x"});
entry.Update({"x"}, 8);
log.Flush();
ASSERT_EQ(data.size(), 55u);
entry.Update({"y"}, 9);
log.Flush();
ASSERT_EQ(data.size(), 68u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<std::string>{"y"});
entry.Update(std::span<const std::string_view>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 76u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<std::string>{});
}
TEST_F(DataLogTest, StructA) {
[[maybe_unused]]
wpi::log::StructLogEntry<ThingA> entry0;
wpi::log::StructLogEntry<ThingA> entry{log, "a", 5};
entry.Append(ThingA{});
entry.Append(ThingA{}, 7);
}
TEST_F(DataLogTest, StructUpdate) {
wpi::log::StructLogEntry<ThingA> entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update(ThingA{}, 7);
log.Flush();
ASSERT_EQ(data.size(), 122u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), ThingA{});
entry.Update(ThingA{}, 8);
log.Flush();
ASSERT_EQ(data.size(), 122u);
entry.Update(ThingA{.x = 1}, 9);
log.Flush();
ASSERT_EQ(data.size(), 127u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), ThingA{.x = 1});
}
TEST_F(DataLogTest, StructArrayA) {
[[maybe_unused]]
wpi::log::StructArrayLogEntry<ThingA> entry0;
wpi::log::StructArrayLogEntry<ThingA> entry{log, "a", 5};
entry.Append({{ThingA{}, ThingA{}}});
entry.Append({{ThingA{}, ThingA{}}}, 7);
}
TEST_F(DataLogTest, StructArrayUpdate) {
wpi::log::StructArrayLogEntry<ThingA> entry{log, "a", 5};
ASSERT_FALSE(entry.GetLastValue().has_value());
entry.Update({{ThingA{}, ThingA{.x = 1}}}, 7);
log.Flush();
ASSERT_EQ(data.size(), 125u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(),
(std::vector<ThingA>{ThingA{}, ThingA{.x = 1}}));
entry.Update({{ThingA{}, ThingA{.x = 1}}}, 8);
log.Flush();
ASSERT_EQ(data.size(), 125u);
entry.Update({{ThingA{}, ThingA{.x = 2}}}, 9);
log.Flush();
ASSERT_EQ(data.size(), 131u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(),
(std::vector<ThingA>{ThingA{}, ThingA{.x = 2}}));
entry.Update(std::span<const ThingA>{}, 10);
log.Flush();
ASSERT_EQ(data.size(), 135u);
ASSERT_TRUE(entry.GetLastValue().has_value());
ASSERT_EQ(entry.GetLastValue().value(), std::vector<ThingA>{});
}
TEST_F(DataLogTest, StructFixedArrayA) {
[[maybe_unused]]
wpi::log::StructArrayLogEntry<std::array<ThingA, 2>> entry0;
wpi::log::StructLogEntry<std::array<ThingA, 2>> entry{log, "a", 5};
std::array<ThingA, 2> arr;
entry.Append(arr);
entry.Append(arr, 7);
}
TEST_F(DataLogTest, StructB) {
Info1 info;
[[maybe_unused]]
wpi::log::StructLogEntry<ThingB, Info1> entry0;
wpi::log::StructLogEntry<ThingB, Info1> entry{log, "b", info, 5};
entry.Append(ThingB{});
entry.Append(ThingB{}, 7);
}
TEST_F(DataLogTest, StructArrayB) {
Info1 info;
[[maybe_unused]]
wpi::log::StructArrayLogEntry<ThingB, Info1> entry0;
wpi::log::StructArrayLogEntry<ThingB, Info1> entry{log, "a", info, 5};
entry.Append({{ThingB{}, ThingB{}}});
entry.Append({{ThingB{}, ThingB{}}}, 7);
}
TEST_F(DataLogTest, StructFixedArrayB) {
Info1 info;
wpi::log::StructLogEntry<std::array<ThingB, 2>, Info1> entry{log, "a", info,
5};
std::array<ThingB, 2> arr;
entry.Append(arr);
entry.Append(arr, 7);
}
TEST_F(DataLogTest, StructC) {
{
wpi::log::StructLogEntry<ThingC> entry{log, "c", 5};
entry.Append(ThingC{});
entry.Append(ThingC{}, 7);
}
{
Info1 info;
wpi::log::StructLogEntry<ThingC, Info1> entry{log, "c1", info, 5};
entry.Append(ThingC{});
entry.Append(ThingC{}, 7);
}
{
Info2 info;
wpi::log::StructLogEntry<ThingC, Info2> entry{log, "c2", info, 5};
entry.Append(ThingC{});
entry.Append(ThingC{}, 7);
}
}
TEST_F(DataLogTest, StructArrayC) {
{
wpi::log::StructArrayLogEntry<ThingC> entry{log, "c", 5};
entry.Append({{ThingC{}, ThingC{}}});
entry.Append({{ThingC{}, ThingC{}}}, 7);
}
{
Info1 info;
wpi::log::StructArrayLogEntry<ThingC, Info1> entry{log, "c1", info, 5};
entry.Append({{ThingC{}, ThingC{}}});
entry.Append({{ThingC{}, ThingC{}}}, 7);
}
{
Info2 info;
wpi::log::StructArrayLogEntry<ThingC, Info2> entry{log, "c2", info, 5};
entry.Append({{ThingC{}, ThingC{}}});
entry.Append({{ThingC{}, ThingC{}}}, 7);
}
}
TEST_F(DataLogTest, StructFixedArrayC) {
std::array<ThingC, 2> arr;
{
wpi::log::StructLogEntry<std::array<ThingC, 2>> entry{log, "c", 5};
entry.Append(arr);
entry.Append(arr, 7);
}
{
Info1 info;
wpi::log::StructLogEntry<std::array<ThingC, 2>, Info1> entry{log, "c1",
info, 5};
entry.Append(arr);
entry.Append(arr, 7);
}
{
Info2 info;
wpi::log::StructLogEntry<std::array<ThingC, 2>, Info2> entry{log, "c2",
info, 5};
entry.Append(arr);
entry.Append(arr, 7);
}
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <string>
#include <string_view>
#include <vector>
#include <gtest/gtest.h>
#include "wpi/datalog/FileLogger.h"
TEST(FileLoggerTest, BufferSingleLine) {
std::vector<std::string> buf;
auto func = wpi::log::FileLogger::Buffer(
[&buf](std::string_view line) { buf.emplace_back(line); });
func("qwertyuiop\n");
EXPECT_EQ("qwertyuiop", buf[0]);
}
TEST(FileLoggerTest, BufferMultiLine) {
std::vector<std::string> buf;
auto func = wpi::log::FileLogger::Buffer(
[&buf](std::string_view line) { buf.emplace_back(line); });
func("line 1\nline 2\nline 3\n");
EXPECT_EQ("line 1\nline 2\nline 3", buf[0]);
}
TEST(FileLoggerTest, BufferPartials) {
std::vector<std::string> buf;
auto func = wpi::log::FileLogger::Buffer(
[&buf](std::string_view line) { buf.emplace_back(line); });
func("part 1");
func("part 2\npart 3");
EXPECT_EQ("part 1part 2", buf[0]);
func("\n");
EXPECT_EQ("part 3", buf[1]);
}
TEST(FileLoggerTest, BufferMultiplePartials) {
std::vector<std::string> buf;
auto func = wpi::log::FileLogger::Buffer(
[&buf](std::string_view line) { buf.emplace_back(line); });
func("part 1");
func("part 2");
func("part 3");
func("part 4\n");
EXPECT_EQ("part 1part 2part 3part 4", buf[0]);
}
TEST(FileLoggerTest, BufferMultipleMultiLinePartials) {
std::vector<std::string> buf;
auto func = wpi::log::FileLogger::Buffer(
[&buf](std::string_view line) { buf.emplace_back(line); });
func("part 1");
func("part 2\npart 3");
func("part 4\n");
EXPECT_EQ("part 1part 2", buf[0]);
EXPECT_EQ("part 3part 4", buf[1]);
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <gtest/gtest.h>
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}