Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildSrc/src/main/groovy/java-common-conventions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ spotless {

// define the steps to apply to those files
trimTrailingWhitespace()
indentWithSpaces()
leadingTabsToSpaces()
endWithNewline()
}
groovyGradle {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Copyright 2026 Prospect Robotics SWENext Club

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.team2813.lib2813.testing.truth;

import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.support.ParameterDeclarations;

interface Component {

Type getType();

enum Type {
TRANSLATION,
ROTATION
}

abstract class ComponentArgumentsProvider<T extends Component> implements ArgumentsProvider {
private final List<T> values;

protected ComponentArgumentsProvider(Type componentType, Stream<T> allValues) {
values = allValues.filter(c -> c.getType() == componentType).toList();
}

@Override
public final Stream<? extends Arguments> provideArguments(
ParameterDeclarations parameters, ExtensionContext context) {
return values.stream().map(Arguments::of);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2026 Prospect Robotics SWENext Club

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.team2813.lib2813.testing.truth;

import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import java.util.stream.Stream;

/** Represents component in a two-dimensional coordinate system. */
enum Pose2dComponent implements Component {
X(Type.TRANSLATION) {
@Override
Pose2d add(Pose2d pose, double value) {
return new Pose2d(pose.getX() + value, pose.getY(), pose.getRotation());
}
},
Y(Type.TRANSLATION) {
@Override
Pose2d add(Pose2d pose, double value) {
return new Pose2d(pose.getX(), pose.getY() + value, pose.getRotation());
}
},
R(Type.ROTATION) {
@Override
Pose2d add(Pose2d pose, double value) {
return new Pose2d(
pose.getTranslation(), new Rotation2d(pose.getRotation().getRadians() + value));
}
};

/** An arguments provider for Pose3dComponent values that represent translations. */
static class TranslationsArgumentsProvider extends ComponentArgumentsProvider<Pose2dComponent> {
TranslationsArgumentsProvider() {
super(Type.TRANSLATION, Stream.of(Pose2dComponent.values()));
}
}

private final Type componentType;

Pose2dComponent(Type componentType) {
this.componentType = componentType;
}

@Override
public final Type getType() {
return componentType;
}

abstract Pose2d add(Pose2d pose, double value);

final Translation2d add(Translation2d translation, double value) {
Pose2d pose = new Pose2d(translation, Rotation2d.kZero);
pose = add(pose, value);
return pose.getTranslation();
}

final Rotation2d add(Rotation2d rotation, double value) {
Pose2d pose = new Pose2d(Translation2d.kZero, rotation);
pose = add(pose, value);
return pose.getRotation();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright 2026 Prospect Robotics SWENext Club

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.team2813.lib2813.testing.truth;

import static org.junit.jupiter.api.Assertions.assertThrows;

import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

/** Tests for {@link Pose2dSubject}. */
class Pose2dSubjectTest {
private static final Pose2d POSE = new Pose2d(7.353, 0.706, new Rotation2d(Math.PI / 6));

@ParameterizedTest
@EnumSource(Pose2dComponent.class)
public void isWithin_valueWithinTolerance_doesNotThrow(Pose2dComponent component) {
Pose2d closePose = component.add(POSE, 0.009);

Pose2dSubject.assertThat(closePose).isWithin(0.01).of(POSE);
}

@ParameterizedTest
@EnumSource(Pose2dComponent.class)
public void isWithin_valueNotWithinTolerance_throws(Pose2dComponent component) {
Pose2d closePose = component.add(POSE, 0.011);

assertThrows(
AssertionError.class, () -> Pose2dSubject.assertThat(closePose).isWithin(0.01).of(POSE));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2026 Prospect Robotics SWENext Club

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.team2813.lib2813.testing.truth;

import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Translation3d;
import java.util.stream.Stream;

/** Represents component in a three-dimensional coordinate system. */
enum Pose3dComponent implements Component {
X(Type.TRANSLATION) {
@Override
Pose3d add(Pose3d pose, double value) {
return new Pose3d(pose.getX() + value, pose.getY(), pose.getZ(), pose.getRotation());
}
},
Y(Type.TRANSLATION) {
@Override
Pose3d add(Pose3d pose, double value) {
return new Pose3d(pose.getX(), pose.getY() + value, pose.getZ(), pose.getRotation());
}
},
Z(Type.TRANSLATION) {
@Override
Pose3d add(Pose3d pose, double value) {
return new Pose3d(pose.getX(), pose.getY(), pose.getZ() + value, pose.getRotation());
}
},
ROLL(Type.ROTATION) {
@Override
Pose3d add(Pose3d pose, double value) {
Rotation3d rotation = pose.getRotation();
return new Pose3d(
pose.getTranslation(),
new Rotation3d(rotation.getX() + value, rotation.getY(), rotation.getZ()));
}
},
PITCH(Type.ROTATION) {
@Override
Pose3d add(Pose3d pose, double value) {
Rotation3d rotation = pose.getRotation();
return new Pose3d(
pose.getTranslation(),
new Rotation3d(rotation.getX(), rotation.getY() + value, rotation.getZ()));
}
},
YAW(Type.ROTATION) {
@Override
Pose3d add(Pose3d pose, double value) {
Rotation3d rotation = pose.getRotation();
return new Pose3d(
pose.getTranslation(),
new Rotation3d(rotation.getX(), rotation.getY(), rotation.getZ() + value));
}
};

/** An arguments provider for Pose3dComponent values that represent translations. */
static class TranslationsArgumentsProvider extends ComponentArgumentsProvider<Pose3dComponent> {
TranslationsArgumentsProvider() {
super(Type.TRANSLATION, Stream.of(Pose3dComponent.values()));
}
}

/** An arguments provider for Pose3dComponent values that represent rotations. */
static class RotationsArgumentsProvider extends ComponentArgumentsProvider<Pose3dComponent> {
RotationsArgumentsProvider() {
super(Type.ROTATION, Stream.of(Pose3dComponent.values()));
}
}

private final Type componentType;

Pose3dComponent(Type componentType) {
this.componentType = componentType;
}

@Override
public final Type getType() {
return componentType;
}

abstract Pose3d add(Pose3d pose, double value);

final Translation3d add(Translation3d translation, double value) {
Pose3d pose = new Pose3d(translation, Rotation3d.kZero);
pose = add(pose, value);
return pose.getTranslation();
}

final Rotation3d add(Rotation3d rotation, double value) {
Pose3d pose = new Pose3d(Translation3d.kZero, rotation);
pose = add(pose, value);
return pose.getRotation();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright 2026 Prospect Robotics SWENext Club

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.team2813.lib2813.testing.truth;

import static org.junit.jupiter.api.Assertions.*;

import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation3d;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

/** Tests for {@link Pose3dSubject}. */
class Pose3dSubjectTest {
private static final Pose3d POSE =
new Pose3d(7.353, 0.706, 42.00, new Rotation3d(6.81, -25.67, 3.16));

@ParameterizedTest
@EnumSource(Pose3dComponent.class)
public void isWithin_valueWithinTolerance_doesNotThrow(Pose3dComponent component) {
Pose3d closePose = component.add(POSE, 0.009);

Pose3dSubject.assertThat(closePose).isWithin(0.01).of(POSE);
}

@ParameterizedTest
@EnumSource(Pose3dComponent.class)
public void isWithin_valueNotWithinTolerance_throws(Pose3dComponent component) {
Pose3d closePose = component.add(POSE, 0.011);

assertThrows(
AssertionError.class, () -> Pose3dSubject.assertThat(closePose).isWithin(0.01).of(POSE));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright 2026 Prospect Robotics SWENext Club

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.team2813.lib2813.testing.truth;

import static org.junit.jupiter.api.Assertions.*;

import edu.wpi.first.math.geometry.Rotation2d;
import org.junit.jupiter.api.Test;

/** Tests for {@link Rotation2dSubject}. */
class Rotation2dSubjectTest {
private static final Rotation2d ROTATION = new Rotation2d(Math.PI / 6);

@Test
public void isWithin_valueWithinTolerance_doesNotThrow() {
Rotation2d closeRotation = Pose2dComponent.R.add(ROTATION, 0.009);

Rotation2dSubject.assertThat(closeRotation).isWithin(0.01).of(ROTATION);
}

@Test
public void isWithin_valueNotWithinTolerance_throws() {
Rotation2d closeRotation = Pose2dComponent.R.add(ROTATION, 0.011);

assertThrows(
AssertionError.class,
() -> Rotation2dSubject.assertThat(closeRotation).isWithin(0.01).of(ROTATION));
}
}
Loading