Skip to content

Commit 07318c2

Browse files
klueverGoogle Java Core Libraries
authored andcommitted
Pretty-print integral numbers in failure messages for isWithin assertions.
RELNOTES=Changed failure messages for `isWithin` assertions to pretty-print integral numbers. PiperOrigin-RevId: 721576493
1 parent 996fa8f commit 07318c2

File tree

6 files changed

+148
-33
lines changed

6 files changed

+148
-33
lines changed

core/src/main/java/com/google/common/truth/Fact.java

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package com.google.common.truth;
1818

19+
import static com.google.common.base.Preconditions.checkArgument;
1920
import static com.google.common.base.Preconditions.checkNotNull;
2021
import static com.google.common.base.Strings.padEnd;
22+
import static com.google.common.base.Strings.padStart;
2123
import static java.lang.Math.max;
2224

2325
import com.google.common.collect.ImmutableList;
@@ -40,7 +42,7 @@ public final class Fact implements Serializable {
4042
* value." The value is converted to a string by calling {@code String.valueOf} on it.
4143
*/
4244
public static Fact fact(String key, @Nullable Object value) {
43-
return new Fact(key, String.valueOf(value));
45+
return new Fact(key, String.valueOf(value), false);
4446
}
4547

4648
/**
@@ -59,15 +61,59 @@ public static Fact fact(String key, @Nullable Object value) {
5961
* </ul>
6062
*/
6163
public static Fact simpleFact(String key) {
62-
return new Fact(key, null);
64+
return new Fact(key, null, false);
65+
}
66+
67+
/**
68+
* Creates a fact with the given key and value, which will be printed in a format like "key:
69+
* value." The numeric value is converted to a string with delimiting commas.
70+
*/
71+
static Fact numericFact(String key, @Nullable Long value) {
72+
return new Fact(key, formatNumericValue(value), true);
73+
}
74+
75+
/**
76+
* Creates a fact with the given key and value, which will be printed in a format like "key:
77+
* value." The numeric value is converted to a string with delimiting commas.
78+
*/
79+
static Fact numericFact(String key, @Nullable Integer value) {
80+
return new Fact(key, formatNumericValue(value), true);
81+
}
82+
83+
static String formatNumericValue(@Nullable Object value) {
84+
if (value == null) {
85+
return "null";
86+
}
87+
88+
// We only support Long and Integer for now; maybe FP numbers in the future?
89+
checkArgument(value instanceof Long || value instanceof Integer);
90+
91+
// DecimalFormat is not available on all platforms
92+
String stringValue = String.valueOf(value);
93+
94+
boolean isNegative = stringValue.startsWith("-");
95+
if (isNegative) {
96+
stringValue = stringValue.substring(1);
97+
}
98+
99+
StringBuilder builder = new StringBuilder();
100+
for (int i = 0; i < stringValue.length(); i++) {
101+
builder.append(stringValue.charAt(i));
102+
if ((stringValue.length() - i - 1) % 3 == 0 && i != stringValue.length() - 1) {
103+
builder.append(',');
104+
}
105+
}
106+
return isNegative ? "-" + builder : builder.toString();
63107
}
64108

65109
final String key;
66110
final @Nullable String value;
111+
final boolean padStart;
67112

68-
private Fact(String key, @Nullable String value) {
113+
private Fact(String key, @Nullable String value, boolean padStart) {
69114
this.key = checkNotNull(key);
70115
this.value = value;
116+
this.padStart = padStart;
71117
}
72118

73119
/**
@@ -86,10 +132,14 @@ public String toString() {
86132
*/
87133
static String makeMessage(ImmutableList<String> messages, ImmutableList<Fact> facts) {
88134
int longestKeyLength = 0;
135+
int longestValueLength = 0;
89136
boolean seenNewlineInValue = false;
90137
for (Fact fact : facts) {
91138
if (fact.value != null) {
92139
longestKeyLength = max(longestKeyLength, fact.key.length());
140+
if (fact.padStart) {
141+
longestValueLength = max(longestValueLength, fact.value.length());
142+
}
93143
// TODO(cpovirk): Look for other kinds of newlines.
94144
seenNewlineInValue |= fact.value.contains("\n");
95145
}
@@ -121,7 +171,11 @@ static String makeMessage(ImmutableList<String> messages, ImmutableList<Fact> fa
121171
} else {
122172
builder.append(padEnd(fact.key, longestKeyLength, ' '));
123173
builder.append(": ");
124-
builder.append(fact.value);
174+
if (fact.padStart) {
175+
builder.append(padStart(fact.value, longestValueLength, ' '));
176+
} else {
177+
builder.append(fact.value);
178+
}
125179
}
126180
builder.append('\n');
127181
}

core/src/main/java/com/google/common/truth/IntegerSubject.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import static com.google.common.base.Preconditions.checkArgument;
1919
import static com.google.common.base.Preconditions.checkNotNull;
20-
import static com.google.common.truth.Fact.fact;
20+
import static com.google.common.truth.Fact.numericFact;
2121
import static com.google.common.truth.MathUtil.equalWithinTolerance;
2222

2323
import org.jspecify.annotations.Nullable;
@@ -101,9 +101,9 @@ public void of(int expected) {
101101

102102
if (!equalWithinTolerance(actual, expected, tolerance)) {
103103
failWithoutActual(
104-
fact("expected", Integer.toString(expected)),
105-
butWas(),
106-
fact("outside tolerance", Integer.toString(tolerance)));
104+
numericFact("expected", expected),
105+
numericFact("but was", actual),
106+
numericFact("outside tolerance", tolerance));
107107
}
108108
}
109109
};
@@ -128,9 +128,9 @@ public void of(int expected) {
128128

129129
if (equalWithinTolerance(actual, expected, tolerance)) {
130130
failWithoutActual(
131-
fact("expected not to be", Integer.toString(expected)),
132-
butWas(),
133-
fact("within tolerance", Integer.toString(tolerance)));
131+
numericFact("expected not to be", expected),
132+
numericFact("but was", actual),
133+
numericFact("within tolerance", tolerance));
134134
}
135135
}
136136
};

core/src/main/java/com/google/common/truth/LongSubject.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import static com.google.common.base.Preconditions.checkArgument;
1919
import static com.google.common.base.Preconditions.checkNotNull;
20-
import static com.google.common.truth.Fact.fact;
20+
import static com.google.common.truth.Fact.numericFact;
2121
import static com.google.common.truth.MathUtil.equalWithinTolerance;
2222

2323
import org.jspecify.annotations.Nullable;
@@ -102,9 +102,9 @@ public void of(long expected) {
102102

103103
if (!equalWithinTolerance(actual, expected, tolerance)) {
104104
failWithoutActual(
105-
fact("expected", Long.toString(expected)),
106-
butWas(),
107-
fact("outside tolerance", Long.toString(tolerance)));
105+
numericFact("expected", expected),
106+
numericFact("but was", actual),
107+
numericFact("outside tolerance", tolerance));
108108
}
109109
}
110110
};
@@ -129,9 +129,9 @@ public void of(long expected) {
129129

130130
if (equalWithinTolerance(actual, expected, tolerance)) {
131131
failWithoutActual(
132-
fact("expected not to be", Long.toString(expected)),
133-
butWas(),
134-
fact("within tolerance", Long.toString(tolerance)));
132+
numericFact("expected not to be", expected),
133+
numericFact("but was", actual),
134+
numericFact("within tolerance", tolerance));
135135
}
136136
}
137137
};

core/src/test/java/com/google/common/truth/FactTest.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
package com.google.common.truth;
1818

1919
import static com.google.common.truth.Fact.fact;
20+
import static com.google.common.truth.Fact.formatNumericValue;
2021
import static com.google.common.truth.Fact.makeMessage;
22+
import static com.google.common.truth.Fact.numericFact;
2123
import static com.google.common.truth.Fact.simpleFact;
2224
import static com.google.common.truth.Truth.assertThat;
25+
import static org.junit.Assert.assertThrows;
2326

27+
import com.google.common.base.Joiner;
2428
import com.google.common.collect.ImmutableList;
2529
import org.junit.Test;
2630
import org.junit.runner.RunWith;
@@ -29,6 +33,9 @@
2933
/** Tests for {@link Fact}. */
3034
@RunWith(JUnit4.class)
3135
public class FactTest {
36+
37+
private static final Joiner TEXT = Joiner.on("\n");
38+
3239
@Test
3340
public void string() {
3441
assertThat(fact("foo", "bar").toString()).isEqualTo("foo: bar");
@@ -51,7 +58,10 @@ public void twoFacts() {
5158
makeMessage(
5259
ImmutableList.<String>of(),
5360
ImmutableList.of(fact("foo", "bar"), fact("longer name", "other value"))))
54-
.isEqualTo("foo : bar\nlonger name: other value");
61+
.isEqualTo(
62+
TEXT.join(
63+
"foo : bar", // force a line break
64+
"longer name: other value"));
5565
}
5666

5767
@Test
@@ -60,10 +70,14 @@ public void numericFacts() {
6070
makeMessage(
6171
ImmutableList.<String>of(),
6272
ImmutableList.of(
63-
fact("expected", 802604),
64-
fact("but was", 773804),
65-
fact("outside tolerance", 9599))))
66-
.isEqualTo("expected : 802604\nbut was : 773804\noutside tolerance: 9599");
73+
numericFact("expected", 802604),
74+
numericFact("but was", 773804),
75+
numericFact("outside tolerance", 9599))))
76+
.isEqualTo(
77+
TEXT.join(
78+
"expected : 802,604",
79+
"but was : 773,804",
80+
"outside tolerance: 9,599"));
6781
}
6882

6983
@Test
@@ -101,4 +115,49 @@ public void withMessage() {
101115
assertThat(makeMessage(ImmutableList.<String>of("hello"), ImmutableList.of(fact("foo", "bar"))))
102116
.isEqualTo("hello\nfoo: bar");
103117
}
118+
119+
@Test
120+
public void formatNumericValue_null() {
121+
assertThat(formatNumericValue(null)).isEqualTo("null");
122+
}
123+
124+
@Test
125+
public void formatNumericValue_zero() {
126+
assertThat(formatNumericValue(0)).isEqualTo("0");
127+
assertThat(formatNumericValue(0L)).isEqualTo("0");
128+
assertThat(formatNumericValue(-0)).isEqualTo("0");
129+
assertThat(formatNumericValue(-0L)).isEqualTo("0");
130+
}
131+
132+
@Test
133+
public void formatNumericValue_positive() {
134+
assertThat(formatNumericValue(9)).isEqualTo("9");
135+
assertThat(formatNumericValue(999L)).isEqualTo("999");
136+
assertThat(formatNumericValue(9599)).isEqualTo("9,599");
137+
assertThat(formatNumericValue(20000L)).isEqualTo("20,000");
138+
assertThat(formatNumericValue(802604)).isEqualTo("802,604");
139+
assertThat(formatNumericValue(1234567890)).isEqualTo("1,234,567,890");
140+
assertThat(formatNumericValue(1234567890L)).isEqualTo("1,234,567,890");
141+
assertThat(formatNumericValue(Integer.MAX_VALUE)).isEqualTo("2,147,483,647");
142+
assertThat(formatNumericValue(Long.MAX_VALUE)).isEqualTo("9,223,372,036,854,775,807");
143+
}
144+
145+
@Test
146+
public void formatNumericValue_negative() {
147+
assertThat(formatNumericValue(-9)).isEqualTo("-9");
148+
assertThat(formatNumericValue(-999L)).isEqualTo("-999");
149+
assertThat(formatNumericValue(-9599)).isEqualTo("-9,599");
150+
assertThat(formatNumericValue(-20000L)).isEqualTo("-20,000");
151+
assertThat(formatNumericValue(-802604)).isEqualTo("-802,604");
152+
assertThat(formatNumericValue(-1234567890)).isEqualTo("-1,234,567,890");
153+
assertThat(formatNumericValue(-1234567890L)).isEqualTo("-1,234,567,890");
154+
assertThat(formatNumericValue(Integer.MIN_VALUE)).isEqualTo("-2,147,483,648");
155+
assertThat(formatNumericValue(Long.MIN_VALUE)).isEqualTo("-9,223,372,036,854,775,808");
156+
}
157+
158+
@Test
159+
public void formatNumericValue_throwsExceptionForNonNumericValue() {
160+
assertThrows(IllegalArgumentException.class, () -> formatNumericValue("foo"));
161+
assertThrows(IllegalArgumentException.class, () -> formatNumericValue(2.2));
162+
}
104163
}

core/src/test/java/com/google/common/truth/IntegerSubjectTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.google.common.truth;
1717

1818
import static com.google.common.truth.ExpectFailure.assertThat;
19+
import static com.google.common.truth.Fact.formatNumericValue;
1920
import static com.google.common.truth.Truth.assertThat;
2021
import static org.junit.Assert.fail;
2122

@@ -155,9 +156,9 @@ public void invokeAssertion(SimpleSubjectBuilder<IntegerSubject, Integer> expect
155156
.factKeys()
156157
.containsExactly("expected", "but was", "outside tolerance")
157158
.inOrder();
158-
assertThat(failure).factValue("expected").isEqualTo(Integer.toString(expected));
159-
assertThat(failure).factValue("but was").isEqualTo(Integer.toString(actual));
160-
assertThat(failure).factValue("outside tolerance").isEqualTo(Integer.toString(tolerance));
159+
assertThat(failure).factValue("expected").isEqualTo(formatNumericValue(expected));
160+
assertThat(failure).factValue("but was").isEqualTo(formatNumericValue(actual));
161+
assertThat(failure).factValue("outside tolerance").isEqualTo(formatNumericValue(tolerance));
161162
}
162163

163164
@Test
@@ -191,8 +192,8 @@ public void invokeAssertion(SimpleSubjectBuilder<IntegerSubject, Integer> expect
191192
}
192193
};
193194
AssertionError failure = expectFailure(callback);
194-
assertThat(failure).factValue("expected not to be").isEqualTo(Integer.toString(expected));
195-
assertThat(failure).factValue("within tolerance").isEqualTo(Integer.toString(tolerance));
195+
assertThat(failure).factValue("expected not to be").isEqualTo(formatNumericValue(expected));
196+
assertThat(failure).factValue("within tolerance").isEqualTo(formatNumericValue(tolerance));
196197
}
197198

198199
@Test

core/src/test/java/com/google/common/truth/LongSubjectTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.google.common.truth;
1717

1818
import static com.google.common.truth.ExpectFailure.assertThat;
19+
import static com.google.common.truth.Fact.formatNumericValue;
1920
import static com.google.common.truth.Truth.assertThat;
2021
import static org.junit.Assert.fail;
2122

@@ -169,9 +170,9 @@ public void invokeAssertion(SimpleSubjectBuilder<LongSubject, Long> expect) {
169170
.factKeys()
170171
.containsExactly("expected", "but was", "outside tolerance")
171172
.inOrder();
172-
assertThat(failure).factValue("expected").isEqualTo(Long.toString(expected));
173-
assertThat(failure).factValue("but was").isEqualTo(Long.toString(actual));
174-
assertThat(failure).factValue("outside tolerance").isEqualTo(Long.toString(tolerance));
173+
assertThat(failure).factValue("expected").isEqualTo(formatNumericValue(expected));
174+
assertThat(failure).factValue("but was").isEqualTo(formatNumericValue(actual));
175+
assertThat(failure).factValue("outside tolerance").isEqualTo(formatNumericValue(tolerance));
175176
}
176177

177178
@Test
@@ -205,8 +206,8 @@ public void invokeAssertion(SimpleSubjectBuilder<LongSubject, Long> expect) {
205206
}
206207
};
207208
AssertionError failure = expectFailure(callback);
208-
assertThat(failure).factValue("expected not to be").isEqualTo(Long.toString(expected));
209-
assertThat(failure).factValue("within tolerance").isEqualTo(Long.toString(tolerance));
209+
assertThat(failure).factValue("expected not to be").isEqualTo(formatNumericValue(expected));
210+
assertThat(failure).factValue("within tolerance").isEqualTo(formatNumericValue(tolerance));
210211
}
211212

212213
@Test

0 commit comments

Comments
 (0)