SCRIPT Move java files

This commit is contained in:
PJ Reiniger
2025-11-07 19:55:40 -05:00
committed by Peter Johnson
parent 7ca1be9bae
commit c350c5f112
1486 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
// 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.util;
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class ErrorMessagesTest {
@Test
void requireNonNullParamNullTest() {
assertThrows(
NullPointerException.class, () -> requireNonNullParam(null, "testParam", "testMethod"));
}
@Test
void requireNonNullParamNotNullTest() {
assertDoesNotThrow(() -> requireNonNullParam("null", "testParam", "testMethod"));
}
}

View File

@@ -0,0 +1,26 @@
// 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.util;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
public class WPIUtilJNITest {
@Test
public void testEnableMockTime() {
assertDoesNotThrow(WPIUtilJNI::enableMockTime);
}
@Test
public void testSetMockTime() {
assertDoesNotThrow(() -> WPIUtilJNI.setMockTime(0L));
}
@Test
public void testNow() {
assertDoesNotThrow(WPIUtilJNI::now);
}
}

View File

@@ -0,0 +1,153 @@
// 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.util.cleanup;
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 java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
class CleanupPoolTest {
static class AutoCloseableObject implements AutoCloseable {
public boolean m_closed;
@Override
public void close() {
m_closed = true;
}
}
static class AutoCloseableObjectWithCallback implements AutoCloseable {
private final Runnable m_cb;
AutoCloseableObjectWithCallback(Runnable cb) {
m_cb = cb;
}
@Override
public void close() {
m_cb.run();
}
}
static class FailingAutoCloseableObject implements AutoCloseable {
public static final String message = "This is an expected failure";
@Override
public void close() {
throw new RuntimeException(message);
}
}
@Test
void cleanupStackWorks() {
List<AutoCloseableObject> objects = new ArrayList<>();
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
try (CleanupPool pool = new CleanupPool()) {
for (AutoCloseableObject autoCloseableObject : objects) {
pool.register(autoCloseableObject);
}
}
for (AutoCloseableObject autoCloseableObject : objects) {
assertTrue(autoCloseableObject.m_closed);
}
}
@Test
@SuppressWarnings("PMD.AvoidCatchingGenericException")
void cleanupStackWithExceptionNotInCloseWorks() {
List<AutoCloseableObject> objects = new ArrayList<>();
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
String message = "This is a known failure";
try (CleanupPool pool = new CleanupPool()) {
for (AutoCloseableObject autoCloseableObject : objects) {
pool.register(autoCloseableObject);
}
throw new Exception(message);
} catch (Exception e) {
assertEquals(message, e.getMessage());
}
for (AutoCloseableObject autoCloseableObject : objects) {
assertTrue(autoCloseableObject.m_closed);
}
}
@Test
void cleanupStackWithExceptionInCloseWorks() {
List<AutoCloseableObject> objects = new ArrayList<>();
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
try (CleanupPool pool = new CleanupPool()) {
for (AutoCloseableObject autoCloseableObject : objects) {
pool.register(new FailingAutoCloseableObject());
pool.register(autoCloseableObject);
}
}
for (AutoCloseableObject autoCloseableObject : objects) {
assertTrue(autoCloseableObject.m_closed);
}
}
@Test
void cleanupStackRemovalWorks() {
List<AutoCloseableObject> objects = new ArrayList<>();
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
objects.add(new AutoCloseableObject());
try (CleanupPool pool = new CleanupPool()) {
for (AutoCloseableObject autoCloseableObject : objects) {
pool.register(autoCloseableObject);
}
pool.remove(objects.get(0));
}
int idx = 0;
for (AutoCloseableObject autoCloseableObject : objects) {
if (idx == 0) {
assertFalse(autoCloseableObject.m_closed);
} else {
assertTrue(autoCloseableObject.m_closed);
}
idx++;
}
}
@Test
void cleanupStackIsLifo() {
List<AutoCloseableObjectWithCallback> objects = new ArrayList<>();
List<Integer> order = new ArrayList<>();
objects.add(new AutoCloseableObjectWithCallback(() -> order.add(0)));
objects.add(new AutoCloseableObjectWithCallback(() -> order.add(1)));
objects.add(new AutoCloseableObjectWithCallback(() -> order.add(2)));
try (CleanupPool pool = new CleanupPool()) {
for (AutoCloseable autoCloseableObject : objects) {
pool.register(autoCloseableObject);
}
}
assertEquals(order.size(), 3);
assertEquals(order.get(0), 2);
assertEquals(order.get(1), 1);
assertEquals(order.get(2), 0);
}
}

View File

@@ -0,0 +1,62 @@
// 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.util.cleanup;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
class ReflectionCleanupTest {
static class CleanupClass implements AutoCloseable {
public boolean m_closed;
@Override
public void close() {
m_closed = true;
}
}
@SuppressWarnings("PMD.TestClassWithoutTestCases")
static class CleanupTest implements ReflectionCleanup {
public CleanupClass m_class1 = new CleanupClass();
public CleanupClass m_class2 = new CleanupClass();
public Object m_nonCleanupObject = new Object();
public Object m_nullCleanupObject;
@Override
public void close() {
reflectionCleanup(CleanupTest.class);
}
}
static class CleanupTest2 extends CleanupTest {
@SkipCleanup public CleanupClass m_class3 = new CleanupClass();
public CleanupClass m_class4 = new CleanupClass();
@Override
public void close() {
reflectionCleanup(CleanupTest2.class);
}
}
@Test
void cleanupClosesAllFields() {
CleanupTest test = new CleanupTest();
test.close();
assertTrue(test.m_class1.m_closed);
assertTrue(test.m_class2.m_closed);
}
@Test
void cleanupOnlyClosesExplicitClassAndSkipWorks() {
CleanupTest2 test = new CleanupTest2();
test.close();
assertFalse(test.m_class1.m_closed);
assertFalse(test.m_class2.m_closed);
assertFalse(test.m_class3.m_closed);
assertTrue(test.m_class4.m_closed);
}
}

View File

@@ -0,0 +1,211 @@
// 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.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class CircularBufferTest {
private final double[] m_values = {
751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
};
private final double[] m_addFirstOut = {
799.913, 421.125, 22.727, 445.697, 132.344, 716.126, 234.252, 342.657
};
private final double[] m_addLastOut = {
342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
};
@Test
void addFirstTest() {
var queue = new CircularBuffer<Double>(8);
for (double value : m_values) {
queue.addFirst(value);
}
for (int i = 0; i < m_addFirstOut.length; i++) {
assertEquals(m_addFirstOut[i], queue.get(i), 0.00005);
}
}
@Test
void addLastTest() {
var queue = new CircularBuffer<Double>(8);
for (double value : m_values) {
queue.addLast(value);
}
for (int i = 0; i < m_addLastOut.length; i++) {
assertEquals(m_addLastOut[i], queue.get(i), 0.00005);
}
}
@Test
void pushPopTest() {
var queue = new CircularBuffer<Double>(3);
// Insert three elements into the buffer
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
assertEquals(3.0, queue.get(2), 0.00005);
/*
* The buffer is full now, so pushing subsequent elements will overwrite the
* front-most elements.
*/
queue.addLast(4.0); // Overwrite 1 with 4
// The buffer now contains 2, 3, and 4
assertEquals(2.0, queue.get(0), 0.00005);
assertEquals(3.0, queue.get(1), 0.00005);
assertEquals(4.0, queue.get(2), 0.00005);
queue.addLast(5.0); // Overwrite 2 with 5
// The buffer now contains 3, 4, and 5
assertEquals(3.0, queue.get(0), 0.00005);
assertEquals(4.0, queue.get(1), 0.00005);
assertEquals(5.0, queue.get(2), 0.00005);
assertEquals(5.0, queue.removeLast(), 0.00005); // 5 is removed
// The buffer now contains 3 and 4
assertEquals(3.0, queue.get(0), 0.00005);
assertEquals(4.0, queue.get(1), 0.00005);
assertEquals(3.0, queue.removeFirst(), 0.00005); // 3 is removed
// Leaving only one element with value == 4
assertEquals(4.0, queue.get(0), 0.00005);
}
@Test
void resetTest() {
var queue = new CircularBuffer<Double>(5);
for (int i = 0; i < 6; i++) {
queue.addLast((double) i);
}
queue.clear();
assertEquals(0, queue.size());
}
@Test
void resizeTest() {
var queue = new CircularBuffer<Double>(5);
/* Buffer contains {1, 2, 3, _, _}
* ^ front
*/
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {_, 1, 2, 3, _}
* ^ front
*/
queue.addLast(0.0);
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
queue.removeFirst();
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {_, _, 1, 2, 3}
* ^ front
*/
queue.addLast(0.0);
queue.addLast(0.0);
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
queue.removeFirst();
queue.removeFirst();
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {3, _, _, 1, 2}
* ^ front
*/
queue.addLast(3.0);
queue.addFirst(2.0);
queue.addFirst(1.0);
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {2, 3, _, _, 1}
* ^ front
*/
queue.addLast(2.0);
queue.addLast(3.0);
queue.addFirst(1.0);
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
// Test addLast() after resize
queue.addLast(3.0);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
assertEquals(3.0, queue.get(2), 0.00005);
// Test addFirst() after resize
queue.addFirst(4.0);
assertEquals(4.0, queue.get(0), 0.00005);
assertEquals(1.0, queue.get(1), 0.00005);
assertEquals(2.0, queue.get(2), 0.00005);
assertEquals(3.0, queue.get(3), 0.00005);
}
}

View File

@@ -0,0 +1,213 @@
// 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.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class DoubleCircularBufferTest {
private final double[] m_values = {
751.848, 766.366, 342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
};
private final double[] m_addFirstOut = {
799.913, 421.125, 22.727, 445.697, 132.344, 716.126, 234.252, 342.657
};
private final double[] m_addLastOut = {
342.657, 234.252, 716.126, 132.344, 445.697, 22.727, 421.125, 799.913
};
@Test
void addFirstTest() {
var queue = new DoubleCircularBuffer(8);
for (double value : m_values) {
queue.addFirst(value);
}
for (int i = 0; i < m_addFirstOut.length; i++) {
assertEquals(m_addFirstOut[i], queue.get(i), 0.00005);
}
}
@Test
void addLastTest() {
var queue = new DoubleCircularBuffer(8);
for (double value : m_values) {
queue.addLast(value);
}
for (int i = 0; i < m_addLastOut.length; i++) {
assertEquals(m_addLastOut[i], queue.get(i), 0.00005);
}
}
@Test
void pushPopTest() {
var queue = new DoubleCircularBuffer(3);
// Insert three elements into the buffer
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
assertEquals(3.0, queue.get(2), 0.00005);
/*
* The buffer is full now, so pushing subsequent elements will overwrite the
* front-most elements.
*/
queue.addLast(4.0); // Overwrite 1 with 4
// The buffer now contains 2, 3, and 4
assertEquals(2.0, queue.get(0), 0.00005);
assertEquals(3.0, queue.get(1), 0.00005);
assertEquals(4.0, queue.get(2), 0.00005);
queue.addLast(5.0); // Overwrite 2 with 5
// The buffer now contains 3, 4, and 5
assertEquals(3.0, queue.get(0), 0.00005);
assertEquals(4.0, queue.get(1), 0.00005);
assertEquals(5.0, queue.get(2), 0.00005);
assertEquals(5.0, queue.removeLast(), 0.00005); // 5 is removed
// The buffer now contains 3 and 4
assertEquals(3.0, queue.get(0), 0.00005);
assertEquals(4.0, queue.get(1), 0.00005);
assertEquals(3.0, queue.removeFirst(), 0.00005); // 3 is removed
// Leaving only one element with value == 4
assertEquals(4.0, queue.get(0), 0.00005);
}
@Test
void resetTest() {
var queue = new DoubleCircularBuffer(5);
for (int i = 0; i < 6; i++) {
queue.addLast(i);
}
queue.clear();
for (int i = 0; i < 5; i++) {
assertEquals(0.0, queue.get(i), 0.00005);
}
}
@Test
void resizeTest() {
var queue = new DoubleCircularBuffer(5);
/* Buffer contains {1, 2, 3, _, _}
* ^ front
*/
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {_, 1, 2, 3, _}
* ^ front
*/
queue.addLast(0.0);
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
queue.removeFirst();
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {_, _, 1, 2, 3}
* ^ front
*/
queue.addLast(0.0);
queue.addLast(0.0);
queue.addLast(1.0);
queue.addLast(2.0);
queue.addLast(3.0);
queue.removeFirst();
queue.removeFirst();
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {3, _, _, 1, 2}
* ^ front
*/
queue.addLast(3.0);
queue.addFirst(2.0);
queue.addFirst(1.0);
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.clear();
/* Buffer contains {2, 3, _, _, 1}
* ^ front
*/
queue.addLast(2.0);
queue.addLast(3.0);
queue.addFirst(1.0);
queue.resize(2);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
queue.resize(5);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
// Test addLast() after resize
queue.addLast(3.0);
assertEquals(1.0, queue.get(0), 0.00005);
assertEquals(2.0, queue.get(1), 0.00005);
assertEquals(3.0, queue.get(2), 0.00005);
// Test addFirst() after resize
queue.addFirst(4.0);
assertEquals(4.0, queue.get(0), 0.00005);
assertEquals(1.0, queue.get(1), 0.00005);
assertEquals(2.0, queue.get(2), 0.00005);
assertEquals(3.0, queue.get(3), 0.00005);
}
}

View File

@@ -0,0 +1,580 @@
// 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.util.struct;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@SuppressWarnings("AvoidEscapedUnicodeCharacters")
class DynamicStructTest {
@SuppressWarnings("MemberName")
private StructDescriptorDatabase db;
@BeforeEach
public void init() {
db = new StructDescriptorDatabase();
}
@Test
void testEmpty() {
var desc = assertDoesNotThrow(() -> db.add("test", ""));
assertEquals(desc.getName(), "test");
assertEquals(desc.getSchema(), "");
assertTrue(desc.getFields().isEmpty());
assertTrue(desc.isValid());
assertEquals(desc.getSize(), 0);
}
@Test
void testNestedStruct() {
var desc = assertDoesNotThrow(() -> db.add("test", "int32 a"));
assertTrue(desc.isValid());
var desc2 = assertDoesNotThrow(() -> db.add("test2", "test a"));
assertTrue(desc2.isValid());
assertEquals(desc2.getSize(), 4);
}
@Test
void testDelayedValid() {
final var desc = assertDoesNotThrow(() -> db.add("test", "foo a"));
assertFalse(desc.isValid());
var desc2 = assertDoesNotThrow(() -> db.add("test2", "foo a; foo b;"));
assertFalse(desc2.isValid());
var desc3 = assertDoesNotThrow(() -> db.add("test3", "foo a[2]"));
assertFalse(desc3.isValid());
var desc4 = assertDoesNotThrow(() -> db.add("foo", "int32 a"));
assertTrue(desc4.isValid());
assertTrue(desc.isValid());
assertEquals(desc.getSize(), 4);
assertTrue(desc2.isValid());
assertEquals(desc2.getSize(), 8);
assertTrue(desc3.isValid());
assertEquals(desc3.getSize(), 8);
}
@Test
void testReuseNestedStructDelayed() {
var desc2 = assertDoesNotThrow(() -> db.add("test2", "test a;test b;"));
var desc = assertDoesNotThrow(() -> db.add("test", "int32 a; uint16 b; int16 c;"));
assertTrue(desc.isValid());
assertTrue(desc2.isValid());
assertEquals(desc2.getSize(), 16);
var fields = desc2.getFields();
var field = fields.get(0);
assertEquals(field.getOffset(), 0);
assertEquals(field.getName(), "a");
field = fields.get(1);
assertEquals(field.getOffset(), 8);
assertEquals(field.getName(), "b");
}
@Test
void testInvalidBitfield() {
assertThrows(
BadSchemaException.class,
() -> db.add("test", "float a:1"),
"field a: type float cannot be bitfield");
assertThrows(
BadSchemaException.class,
() -> db.add("test", "double a:1"),
"field a: type double cannot be bitfield");
assertThrows(
BadSchemaException.class,
() -> db.add("test", "foo a:1"),
"field a: type foo cannot be bitfield");
}
@Test
void testCircularStructReference() {
assertThrows(
BadSchemaException.class,
() -> db.add("test", "test a"),
"field a: recursive struct reference");
}
@Test
void testNestedCircularStructRef() {
assertDoesNotThrow(() -> db.add("test", "foo a"));
assertDoesNotThrow(() -> db.add("foo", "bar a"));
assertThrows(
BadSchemaException.class,
() -> db.add("bar", "test a"),
"circular struct reference: bar <- foo <- test");
// ok
var desc = assertDoesNotThrow(() -> db.add("baz", "bar a"));
assertFalse(desc.isValid());
}
@Test
void testNestedCircularStructRef2() {
assertDoesNotThrow(() -> db.add("test", "foo a"));
assertDoesNotThrow(() -> db.add("bar", "test a"));
assertThrows(
BadSchemaException.class,
() -> db.add("foo", "bar a"),
"circular struct reference: foo <- test <- bar");
}
@Test
void testBitfieldBasic() {
var desc = assertDoesNotThrow(() -> db.add("test", "int32 a:2; uint32 b:30"));
assertEquals(desc.getSize(), 4);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 2);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x3);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 4);
field = fields.get(1);
assertEquals(field.getBitWidth(), 30);
assertEquals(field.getBitShift(), 2);
assertEquals(field.getBitMask(), 0x3fffffff);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 4);
}
@Test
void testBitfieldDiffType() {
var desc = assertDoesNotThrow(() -> db.add("test", "int32 a:2; int16 b:2"));
assertEquals(desc.getSize(), 6);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 2);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x3);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 4);
field = fields.get(1);
assertEquals(field.getBitWidth(), 2);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x3);
assertEquals(field.getOffset(), 4);
assertEquals(field.getSize(), 2);
}
@Test
void testBitfieldOverflow() {
var desc = assertDoesNotThrow(() -> db.add("test", "int8 a:4; int8 b:5"));
assertEquals(desc.getSize(), 2);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 4);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0xf);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 1);
field = fields.get(1);
assertEquals(field.getBitWidth(), 5);
assertEquals(field.getBitMask(), 0x1f);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getOffset(), 1);
assertEquals(field.getSize(), 1);
}
@Test
void testBitfieldBoolBegin8() {
var desc = assertDoesNotThrow(() -> db.add("test", "bool a:1; int8 b:5"));
assertEquals(desc.getSize(), 1);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 1);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x1);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 1);
field = fields.get(1);
assertEquals(field.getBitWidth(), 5);
assertEquals(field.getBitMask(), 0x1f);
assertEquals(field.getBitShift(), 1);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 1);
}
@Test
void testBitfieldBoolBegin16() {
var desc = assertDoesNotThrow(() -> db.add("test", "bool a:1; int16 b:5"));
assertEquals(desc.getSize(), 3);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 1);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x1);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 1);
field = fields.get(1);
assertEquals(field.getBitWidth(), 5);
assertEquals(field.getBitMask(), 0x1f);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getOffset(), 1);
assertEquals(field.getSize(), 2);
}
@Test
void testBitfieldBoolMid() {
var desc =
assertDoesNotThrow(() -> db.add("test", "int16 a:2; bool b:1; bool c:1; uint16 d:5"));
assertEquals(desc.getSize(), 2);
var fields = desc.getFields();
assertEquals(fields.size(), 4);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 2);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x3);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
field = fields.get(1);
assertEquals(field.getBitWidth(), 1);
assertEquals(field.getBitMask(), 0x1);
assertEquals(field.getBitShift(), 2);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
field = fields.get(2);
assertEquals(field.getBitWidth(), 1);
assertEquals(field.getBitMask(), 0x1);
assertEquals(field.getBitShift(), 3);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
field = fields.get(3);
assertEquals(field.getBitWidth(), 5);
assertEquals(field.getBitMask(), 0x1f);
assertEquals(field.getBitShift(), 4);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
}
@Test
void testBitfieldBoolEnd() {
var desc = assertDoesNotThrow(() -> db.add("test", "int16 a:15; bool b:1"));
assertEquals(desc.getSize(), 2);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 15);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0x7fff);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
field = fields.get(1);
assertEquals(field.getBitWidth(), 1);
assertEquals(field.getBitMask(), 0x1);
assertEquals(field.getBitShift(), 15);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
}
@Test
void testBitfieldBoolEnd2() {
var desc = assertDoesNotThrow(() -> db.add("test", "int16 a:16; bool b:1"));
assertEquals(desc.getSize(), 3);
var fields = desc.getFields();
assertEquals(fields.size(), 2);
var field = fields.get(0);
assertEquals(field.getBitWidth(), 16);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getBitMask(), 0xffff);
assertEquals(field.getOffset(), 0);
assertEquals(field.getSize(), 2);
field = fields.get(1);
assertEquals(field.getBitWidth(), 1);
assertEquals(field.getBitMask(), 0x1);
assertEquals(field.getBitShift(), 0);
assertEquals(field.getOffset(), 2);
assertEquals(field.getSize(), 1);
}
@Test
void testBitfieldBoolWrongSize() {
assertThrows(
BadSchemaException.class,
() -> db.add("test", "bool a:2"),
"field a: bit width must be 1 for bool type");
}
@Test
void testBitfieldTooBig() {
assertThrows(
BadSchemaException.class,
() -> db.add("test", "int16 a:17"),
"field a: bit width 17 exceeds type size");
}
@Test
void testDuplicateFieldName() {
assertThrows(
BadSchemaException.class,
() -> db.add("test", "int16 a; int8 a"),
"field a: duplicate field name");
}
private static Stream<Arguments> provideSimpleTestParams() {
return Stream.of(
Arguments.of("bool a", 1, StructFieldType.kBool, false, false, 8, 0xff, 0, 0),
Arguments.of("char a", 1, StructFieldType.kChar, false, false, 8, 0xff, 0, 0),
Arguments.of("int8 a", 1, StructFieldType.kInt8, true, false, 8, 0xff, -128, 127),
Arguments.of("int16 a", 2, StructFieldType.kInt16, true, false, 16, 0xffff, -32768, 32767),
Arguments.of(
"int32 a",
4,
StructFieldType.kInt32,
true,
false,
32,
0xffffffffL,
-2147483648,
2147483647),
Arguments.of(
"int64 a",
8,
StructFieldType.kInt64,
true,
false,
64,
-1,
-9223372036854775808L,
9223372036854775807L),
Arguments.of("uint8 a", 1, StructFieldType.kUint8, false, true, 8, 0xff, 0, 255),
Arguments.of("uint16 a", 2, StructFieldType.kUint16, false, true, 16, 0xffff, 0, 65535),
Arguments.of(
"uint32 a", 4, StructFieldType.kUint32, false, true, 32, 0xffffffffL, 0, 4294967295L),
Arguments.of("uint64 a", 8, StructFieldType.kUint64, false, true, 64, -1, 0, 0),
Arguments.of("float a", 4, StructFieldType.kFloat, false, false, 32, 0xffffffffL, 0, 0),
Arguments.of("float32 a", 4, StructFieldType.kFloat, false, false, 32, 0xffffffffL, 0, 0),
Arguments.of("double a", 8, StructFieldType.kDouble, false, false, 64, -1, 0, 0),
Arguments.of("float64 a", 8, StructFieldType.kDouble, false, false, 64, -1, 0, 0),
Arguments.of("foo a", 0, StructFieldType.kStruct, false, false, 0, 0, 0, 0));
}
@ParameterizedTest
@MethodSource("provideSimpleTestParams")
void testStandardCheck(
String schema,
int size,
StructFieldType type,
boolean isInt,
boolean isUint,
int bitWidth,
long bitMask) {
var desc = assertDoesNotThrow(() -> db.add("test", schema));
assertEquals(desc.getName(), "test");
assertEquals(desc.getSchema(), schema);
var fields = desc.getFields();
assertEquals(fields.size(), 1);
var field = fields.get(0);
assertEquals(field.getParent(), desc);
assertEquals(field.getName(), "a");
assertEquals(field.isInt(), isInt);
assertEquals(field.isUint(), isUint);
assertFalse(field.isArray());
if (type != StructFieldType.kStruct) {
assertTrue(desc.isValid());
assertEquals(desc.getSize(), size);
assertEquals(field.getSize(), size);
assertEquals(field.getBitWidth(), bitWidth);
assertEquals(field.getBitMask(), bitMask);
} else {
assertFalse(desc.isValid());
assertNotNull(field.getStruct());
}
}
@ParameterizedTest
@MethodSource("provideSimpleTestParams")
void testStandardArray(
String schema,
int size,
StructFieldType type,
boolean isInt,
boolean isUint,
int bitWidth,
long bitMask) {
var desc = assertDoesNotThrow(() -> db.add("test", schema + "[2]"));
assertEquals(desc.getName(), "test");
assertEquals(desc.getSchema(), schema + "[2]");
var fields = desc.getFields();
assertEquals(fields.size(), 1);
var field = fields.get(0);
assertEquals(field.getParent(), desc);
assertEquals(field.getName(), "a");
assertEquals(field.isInt(), isInt);
assertEquals(field.isUint(), isUint);
assertTrue(field.isArray());
assertEquals(field.getArraySize(), 2);
if (type != StructFieldType.kStruct) {
assertTrue(desc.isValid());
assertEquals(desc.getSize(), size * 2);
} else {
assertFalse(desc.isValid());
assertNotNull(field.getStruct());
}
}
@ParameterizedTest
@MethodSource("provideSimpleTestParams")
void testIntRoundTrip(
String schema,
int size,
StructFieldType type,
boolean isInt,
boolean isUint,
int bitWidth,
long bitMask,
long minVal,
long maxVal) {
if (type == StructFieldType.kStruct) {
return;
}
var desc = assertDoesNotThrow(() -> db.add("test", schema));
assertTrue(desc.isValid());
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertNotNull(field);
if ((isInt || isUint) && type != StructFieldType.kUint64) {
// Java can't represent uint64 max
dynamic.setIntField(field, minVal);
assertEquals(minVal, dynamic.getIntField(field));
dynamic.setIntField(field, maxVal);
assertEquals(maxVal, dynamic.getIntField(field));
} else if (type == StructFieldType.kBool) {
dynamic.setBoolField(field, false);
assertFalse(dynamic.getBoolField(field));
dynamic.setBoolField(field, true);
assertTrue(dynamic.getBoolField(field));
}
}
@Test
void testStringAllZeros() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[32]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertEquals("", dynamic.getStringField(field));
}
@Test
void testStringRoundTrip() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[32]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertTrue(dynamic.setStringField(field, "abc"));
assertEquals("abc", dynamic.getStringField(field));
}
@Test
void testStringRoundTripEmbeddedNull() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[32]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertTrue(dynamic.setStringField(field, "ab\0c"));
assertEquals("ab\0c", dynamic.getStringField(field));
}
@Test
void testStringRoundTripStringTooLong() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "abc"));
assertEquals("ab", dynamic.getStringField(field));
}
@Test
void testStringRoundTripPartial2ByteUtf8() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "a\u0234"));
assertEquals("a", dynamic.getStringField(field));
}
@Test
void testStringRoundTrip2ByteUtf8() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[3]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertTrue(dynamic.setStringField(field, "a\u0234"));
assertEquals("a\u0234", dynamic.getStringField(field));
}
@Test
void testStringRoundTripPartial3ByteUtf8FirstByte() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "a\u1234"));
assertEquals("a", dynamic.getStringField(field));
}
@Test
void testStringRoundTripPartial3ByteUtf8SecondByte() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[3]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "a\u1234"));
assertEquals("a", dynamic.getStringField(field));
}
@Test
void testStringRoundTrip3ByteUtf8() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[4]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertTrue(dynamic.setStringField(field, "a\u1234"));
assertEquals("a\u1234", dynamic.getStringField(field));
}
@Test
void testStringRoundTripPartial4ByteUtf8FirstByte() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[2]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "a\uD83D\uDC00"));
assertEquals("a", dynamic.getStringField(field));
}
@Test
void testStringRoundTripPartial4ByteUtf8SecondByte() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[3]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "a\uD83D\uDC00"));
assertEquals("a", dynamic.getStringField(field));
}
@Test
void testStringRoundTripPartial4ByteUtf8ThirdByte() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[4]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertFalse(dynamic.setStringField(field, "a\uD83D\uDC00"));
assertEquals("a", dynamic.getStringField(field));
}
@Test
void testStringRoundTrip4ByteUtf8() {
var desc = assertDoesNotThrow(() -> db.add("test", "char a[5]"));
var dynamic = DynamicStruct.allocate(desc);
var field = desc.findFieldByName("a");
assertTrue(dynamic.setStringField(field, "a\uD83D\uDC00"));
assertEquals("a\uD83D\uDC00", dynamic.getStringField(field));
}
}

View File

@@ -0,0 +1,182 @@
// 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.util.struct;
import static edu.wpi.first.util.struct.StructGenerator.genEnum;
import static edu.wpi.first.util.struct.StructGenerator.genRecord;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.junit.jupiter.api.Test;
class StructGeneratorTest {
public record CustomRecord(int int32, boolean bool, double float64, char character, short int16)
implements StructSerializable {
public static CustomRecord create() {
return new CustomRecord(42, true, Math.PI, 'a', (short) 16);
}
public static final Struct<CustomRecord> struct = genRecord(CustomRecord.class);
@Override
public final boolean equals(Object arg) {
return arg == this
|| arg instanceof CustomRecord other
&& int32 == other.int32
&& bool == other.bool
&& float64 == other.float64
&& character == other.character
&& int16 == other.int16;
}
@Override
public final int hashCode() {
return Integer.hashCode(int32)
+ Boolean.hashCode(bool)
+ Double.hashCode(float64)
+ Character.hashCode(character)
+ Short.hashCode(int16);
}
}
public enum CustomEnum implements StructSerializable {
A(8),
B(16),
C(32),
D(64);
public final int value;
CustomEnum(int value) {
this.value = value;
}
public static final Struct<CustomEnum> struct = genEnum(CustomEnum.class);
}
public enum AnimalEnum implements StructSerializable {
Dog,
Cat;
public static final Struct<AnimalEnum> struct = genEnum(AnimalEnum.class);
}
public record HigherOrderRecord(
CustomRecord procRecord, CustomEnum procEnum, AnimalEnum animal, long i64, byte uint8)
implements StructSerializable {
public static HigherOrderRecord create() {
return new HigherOrderRecord(
CustomRecord.create(), CustomEnum.A, AnimalEnum.Dog, 1234567890123456789L, (byte) 100);
}
public static final Struct<HigherOrderRecord> struct = genRecord(HigherOrderRecord.class);
@Override
public final boolean equals(Object arg) {
return arg == this
|| arg instanceof HigherOrderRecord other
&& procRecord.equals(other.procRecord)
&& procEnum == other.procEnum
&& i64 == other.i64
&& uint8 == other.uint8;
}
@Override
public final int hashCode() {
return procRecord.hashCode()
+ procEnum.hashCode()
+ Long.hashCode(i64)
+ Byte.hashCode(uint8);
}
}
@SuppressWarnings("unchecked")
private <S extends StructSerializable> void testStructRoundTrip(S value) {
Struct<S> struct = StructFetcher.fetchStruct((Class<S>) value.getClass()).get();
ByteBuffer buffer = ByteBuffer.allocate(struct.getSize());
buffer.order(ByteOrder.LITTLE_ENDIAN);
struct.pack(buffer, value);
buffer.rewind();
final S deser = struct.unpack(buffer);
assertEquals(value, deser);
}
@SuppressWarnings("unchecked")
private <S extends StructSerializable> void testStructDoublePack(S value) {
Struct<S> struct = StructFetcher.fetchStruct((Class<S>) value.getClass()).get();
ByteBuffer buffer = ByteBuffer.allocate(struct.getSize());
buffer.order(ByteOrder.LITTLE_ENDIAN);
struct.pack(buffer, value);
buffer.rewind();
struct.pack(buffer, value);
buffer.rewind();
final S deser = struct.unpack(buffer);
assertEquals(value, deser);
}
@SuppressWarnings("unchecked")
private <S extends StructSerializable> void testStructDoubleUnpack(S value) {
Struct<S> struct = StructFetcher.fetchStruct((Class<S>) value.getClass()).get();
ByteBuffer buffer = ByteBuffer.allocate(struct.getSize());
buffer.order(ByteOrder.LITTLE_ENDIAN);
struct.pack(buffer, value);
buffer.rewind();
final S deser = struct.unpack(buffer);
assertEquals(value, deser);
buffer.rewind();
final S deser2 = struct.unpack(buffer);
assertEquals(value, deser2);
}
@Test
void testCustomRecordRoundTrip() {
testStructRoundTrip(CustomRecord.create());
}
@Test
void testCustomRecordDoublePack() {
testStructDoublePack(CustomRecord.create());
}
@Test
void testCustomRecordDoubleUnpack() {
testStructDoubleUnpack(CustomRecord.create());
}
@Test
void testCustomEnumRoundTrip() {
testStructRoundTrip(CustomEnum.A);
}
@Test
void testCustomEnumDoublePack() {
testStructDoublePack(CustomEnum.A);
}
@Test
void testCustomEnumDoubleUnpack() {
testStructDoubleUnpack(CustomEnum.A);
}
@Test
void testHigherOrderRecordRoundTrip() {
testStructRoundTrip(HigherOrderRecord.create());
}
@Test
void testHigherOrderRecordDoublePack() {
testStructDoublePack(HigherOrderRecord.create());
}
@Test
void testHigherOrderRecordDoubleUnpack() {
testStructDoubleUnpack(HigherOrderRecord.create());
}
}

View File

@@ -0,0 +1,209 @@
// 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.util.struct.parser;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
class ParserTest {
@Test
void testEmpty() {
Parser p = new Parser("");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertTrue(schema.declarations.isEmpty());
}
@Test
void testEmptySemicolon() {
Parser p = new Parser(";");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertTrue(schema.declarations.isEmpty());
}
@Test
void testSimple() {
Parser p = new Parser("int32 a");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.arraySize, 1);
}
@Test
void testSimpleTrailingSemi() {
Parser p = new Parser("int32 a;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
}
@Test
void testArray() {
Parser p = new Parser("int32 a[2]");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.arraySize, 2);
}
@Test
void testArrayTrailingSemi() {
Parser p = new Parser("int32 a[2];");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
}
@Test
void testBitfield() {
Parser p = new Parser("int32 a:2");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.bitWidth, 2);
}
@Test
void testBitfieldTrailingSemi() {
Parser p = new Parser("int32 a:2;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
}
@Test
void testEnumKeyword() {
Parser p = new Parser("enum {x=1} int32 a;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.enumValues.size(), 1);
assertEquals(decl.enumValues.get("x"), 1);
}
@Test
void testEnumNoKeyword() {
Parser p = new Parser("{x=1} int32 a;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.enumValues.size(), 1);
assertEquals(decl.enumValues.get("x"), 1);
}
@Test
void testEnumNoValues() {
Parser p = new Parser("{} int32 a;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertTrue(decl.enumValues.isEmpty());
}
@Test
void testEnumMultipleValues() {
Parser p = new Parser("{x=1,y=-2} int32 a;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.enumValues.size(), 2);
assertEquals(decl.enumValues.get("x"), 1);
assertEquals(decl.enumValues.get("y"), -2);
}
@Test
void testEnumTrailingComma() {
Parser p = new Parser("{x=1,y=2,} int32 a;");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 1);
var decl = schema.declarations.get(0);
assertEquals(decl.typeString, "int32");
assertEquals(decl.name, "a");
assertEquals(decl.enumValues.size(), 2);
assertEquals(decl.enumValues.get("x"), 1);
assertEquals(decl.enumValues.get("y"), 2);
}
@Test
void testMultipleNoTrailingSemi() {
Parser p = new Parser("int32 a; int16 b");
ParsedSchema schema = assertDoesNotThrow(p::parse);
assertEquals(schema.declarations.size(), 2);
assertEquals(schema.declarations.get(0).typeString, "int32");
assertEquals(schema.declarations.get(0).name, "a");
assertEquals(schema.declarations.get(1).typeString, "int16");
assertEquals(schema.declarations.get(1).name, "b");
}
@Test
void testErrBitfieldArray() {
Parser p = new Parser("int32 a[1]:2");
assertThrows(ParseException.class, p::parse, "10: expected ';', got ':'");
}
@Test
void testErrNoArrayValue() {
Parser p = new Parser("int32 a[]");
assertThrows(ParseException.class, p::parse, "8: expected integer, got ']'");
}
@Test
void testErrNoBitfieldValue() {
Parser p = new Parser("int32 a:");
assertThrows(ParseException.class, p::parse, "8: expected integer, got ''");
}
@Test
void testErrNoNameArray() {
Parser p = new Parser("int32 [2]");
assertThrows(ParseException.class, p::parse, "6: expected identifier, got '['");
}
@Test
void testErrNoNameBitField() {
Parser p = new Parser("int32 :2");
assertThrows(ParseException.class, p::parse, "6: expected identifier, got ':'");
}
@Test
void testNegativeBitField() {
Parser p = new Parser("int32 a:-1");
assertThrows(
ParseException.class, p::parse, "8: bitfield width '-1' is not a positive integer");
}
@Test
void testNegativeArraySize() {
Parser p = new Parser("int32 a[-1]");
assertThrows(ParseException.class, p::parse, "8: array size '-1' is not a positive integer");
}
@Test
void testZeroBitField() {
Parser p = new Parser("int32 a:0");
assertThrows(ParseException.class, p::parse, "8: bitfield width '0' is not a positive integer");
}
@Test
void testZeroArraySize() {
Parser p = new Parser("int32 a[0]");
assertThrows(ParseException.class, p::parse, "8: array size '0' is not a positive integer");
}
}