tunnel: Add an initial set of unit tests

Includes a control set of broken configuration files that we attempt to parse and
verify that the parser fails in a predictable and consistent manner.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2020-03-14 10:32:12 +05:30
parent 6c8a4a6a28
commit 093139bc91
15 changed files with 323 additions and 13 deletions

View File

@ -9,7 +9,6 @@
</option>
<option name="nonThreadSafeTypes" value="" />
</inspection_tool>
<inspection_tool class="AndroidLintGoogleAppIndexingWarning" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintIconExpectedSize" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintNegativeMargin" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintTypographyQuotes" enabled="true" level="WARNING" enabled_by_default="true" />
@ -40,7 +39,9 @@
<inspection_tool class="AssignmentToLambdaParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToSuperclassField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentUsedAsCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.util.HashMap,put,java.util.Map,put" />
</inspection_tool>
<inspection_tool class="BadExceptionCaught" enabled="true" level="WARNING" enabled_by_default="true">
<option name="exceptionsString" value="" />
<option name="exceptions">
@ -59,6 +60,9 @@
<inspection_tool class="CallToStringConcatCanBeReplacedByOperator" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CannotResolve" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="CastConflictsWithInstanceof" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CatchMayIgnoreException" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreCatchBlocksWithComments" value="false" />
</inspection_tool>
<inspection_tool class="ChainedEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassInitializer" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassNameDiffersFromFileName" enabled="true" level="WARNING" enabled_by_default="true" />
@ -103,9 +107,6 @@
<inspection_tool class="DoubleLiteralMayBeFloatLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicateAlternationBranch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicateBooleanBranch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicateCondition" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSideEffectConditions" value="true" />
</inspection_tool>
<inspection_tool class="DuplicateDeclarations" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="DynamicRegexReplaceableByCompiledPattern" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ElementOnlyUsedFromTestCode" enabled="false" level="WARNING" enabled_by_default="false" />
@ -196,7 +197,6 @@
<inspection_tool class="ImplicitSubclassInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="IncompatibleTypes" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="InitializerIssues" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="InnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
@ -215,7 +215,6 @@
<inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InstantiationOfUtilityClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IntLiteralMayBeLongLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IntegerDivisionInFloatingPointContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IntegerTypeRequired" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="InterfaceNamingConvention" enabled="false" level="WARNING" enabled_by_default="false">
@ -294,6 +293,7 @@
<option name="ignoreForLoopDeclarations" value="true" />
</inspection_tool>
<inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MultipleVariablesInDeclaration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NamedResource" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="NativeMethodNamingConvention" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NegatedConditional" enabled="true" level="WARNING" enabled_by_default="true">
@ -347,7 +347,6 @@
<inspection_tool class="OverriddenMethodCallDuringObjectConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PackageInfoWithoutPackage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PointerTypeRequired" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PointlessNullCheck" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedInnerClass" enabled="true" level="WARNING" enabled_by_default="true">
@ -394,7 +393,13 @@
<inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticCallOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticFieldReferenceOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticImport" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticImport" enabled="true" level="WARNING" enabled_by_default="true">
<option name="allowedClasses">
<set>
<option value="org.junit.Assert" />
</set>
</option>
</inspection_tool>
<inspection_tool class="StaticInheritance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticMethodNamingConvention" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
@ -432,7 +437,6 @@
<inspection_tool class="TemplateArgumentsIssues" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="TestMethodWithoutAssertion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="assertionMethods" value="org.junit.Assert,assert.*|fail.*,junit.framework.Assert,assert.*|fail.*,org.junit.jupiter.api.Assertions,assert.*|fail.*,org.mockito.Mockito,verify.*,org.mockito.InOrder,verify,org.junit.rules.ExpectedException,expect.*,org.hamcrest.MatcherAssert,assertThat" />
<option name="assertKeywordIsAssertion" value="false" />
</inspection_tool>
<inspection_tool class="TestNGMethodNamingConvention" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ThisEscapedInConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
@ -440,7 +444,6 @@
<option name="ignoreRethrownExceptions" value="true" />
</inspection_tool>
<inspection_tool class="ThrowsRuntimeException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ToArrayCallWithZeroLengthArrayArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TooBroadScope" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_allowConstructorAsInitializer" value="false" />
<option name="m_onlyLookAtBlocks" value="true" />
@ -466,10 +469,8 @@
<inspection_tool class="UnnecessaryBlockStatement" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSwitchBranches" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessaryCallToStringValueOf" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryConstantArrayCreationExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryDefault" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryExplicitNumericCast" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryFullyQualifiedName" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreJavadoc" value="false" />

View File

@ -29,6 +29,7 @@ buildscript {
eddsaVersion = '0.3.0'
bintrayPluginVersion = '1.8.4'
mavenPluginVersion = '2.1'
junitVersion = '4.13'
groupName = 'com.wireguard.android'
}

View File

@ -21,6 +21,11 @@ android {
path 'tools/CMakeLists.txt'
}
}
testOptions.unitTests.all {
testLogging {
events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
}
}
}
dependencies {
@ -30,6 +35,7 @@ dependencies {
implementation "com.google.code.findbugs:jsr305:$jsr305Version"
implementation "com.jakewharton.threetenabp:threetenabp:$threetenabpVersion"
implementation "net.i2p.crypto:eddsa:$eddsaVersion"
testImplementation "junit:junit:$junitVersion"
}
apply from: "publish.gradle"

View File

@ -0,0 +1,172 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
import com.wireguard.config.BadConfigException.Location;
import com.wireguard.config.BadConfigException.Reason;
import com.wireguard.config.BadConfigException.Section;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
public class BadConfigExceptionTest {
private static final String[] CONFIG_NAMES = {
"invalid-key",
"invalid-number",
"invalid-value",
"missing-attribute",
"missing-section",
"syntax-error",
"unknown-attribute",
"unknown-section"
};
private static final Map<String, InputStream> CONFIG_MAP = new HashMap<>();
@BeforeClass
public static void readConfigs() {
for (final String config: CONFIG_NAMES) {
CONFIG_MAP.put(config, BadConfigExceptionTest.class.getClassLoader().getResourceAsStream(config + ".conf"));
}
}
@AfterClass
public static void closeStreams() {
for (final InputStream inputStream : CONFIG_MAP.values()) {
try {
inputStream.close();
} catch (final IOException ignored) {
}
}
}
@Test
public void throws_correctly_with_INVALID_KEY_reason() {
try {
Config.parse(CONFIG_MAP.get("invalid-key"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.INVALID_KEY);
assertEquals(e.getLocation(), Location.PUBLIC_KEY);
assertEquals(e.getSection(), Section.PEER);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException thrown during test");
}
}
@Test
public void throws_correctly_with_INVALID_NUMBER_reason() {
try {
Config.parse(CONFIG_MAP.get("invalid-number"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.INVALID_NUMBER);
assertEquals(e.getLocation(), Location.PERSISTENT_KEEPALIVE);
assertEquals(e.getSection(), Section.PEER);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException thrown during test");
}
}
@Test
public void throws_correctly_with_INVALID_VALUE_reason() {
try {
Config.parse(CONFIG_MAP.get("invalid-value"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.INVALID_VALUE);
assertEquals(e.getLocation(), Location.DNS);
assertEquals(e.getSection(), Section.INTERFACE);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException throwing during test");
}
}
@Test
public void throws_correctly_with_MISSING_ATTRIBUTE_reason() {
try {
Config.parse(CONFIG_MAP.get("missing-attribute"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.MISSING_ATTRIBUTE);
assertEquals(e.getLocation(), Location.PUBLIC_KEY);
assertEquals(e.getSection(), Section.PEER);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException throwing during test");
}
}
@Test
public void throws_correctly_with_MISSING_SECTION_reason() {
try {
Config.parse(CONFIG_MAP.get("missing-section"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.MISSING_SECTION);
assertEquals(e.getLocation(), Location.TOP_LEVEL);
assertEquals(e.getSection(), Section.CONFIG);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException throwing during test");
}
}
@Test
public void throws_correctly_with_SYNTAX_ERROR_reason() {
try {
Config.parse(CONFIG_MAP.get("syntax-error"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.SYNTAX_ERROR);
assertEquals(e.getLocation(), Location.TOP_LEVEL);
assertEquals(e.getSection(), Section.PEER);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException throwing during test");
}
}
@Test
public void throws_correctly_with_UNKNOWN_ATTRIBUTE_reason() {
try {
Config.parse(CONFIG_MAP.get("unknown-attribute"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.UNKNOWN_ATTRIBUTE);
assertEquals(e.getLocation(), Location.TOP_LEVEL);
assertEquals(e.getSection(), Section.PEER);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException throwing during test");
}
}
@Test
public void throws_correctly_with_UNKNOWN_SECTION_reason() {
try {
Config.parse(CONFIG_MAP.get("unknown-section"));
fail("Config parsing must fail in this test");
} catch (final BadConfigException e) {
assertEquals(e.getReason(), Reason.UNKNOWN_SECTION);
assertEquals(e.getLocation(), Location.TOP_LEVEL);
assertEquals(e.getSection(), Section.CONFIG);
} catch (final IOException e) {
e.printStackTrace();
fail("IOException throwing during test");
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.config;
import static org.junit.Assert.*;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
public class ConfigTest {
@Test
public void valid_config_parses_correctly() throws IOException, ParseException {
Config config = null;
final Collection<InetNetwork> expectedAllowedIps = new HashSet<>(Arrays.asList(InetNetwork.parse("0.0.0.0/0"), InetNetwork.parse("::0/0")));
try (final InputStream is = Objects.requireNonNull(getClass().getClassLoader()).getResourceAsStream("working.conf")) {
config = Config.parse(is);
} catch (final BadConfigException e) {
fail("'working.conf' should never fail to parse");
}
assertNotNull("config cannot be null after parsing", config);
assertTrue(
"No applications should be excluded by default",
config.getInterface().getExcludedApplications().isEmpty()
);
assertEquals("Test config has exactly one peer", 1, config.getPeers().size());
assertEquals("Test config's allowed IPs are 0.0.0.0/0 and ::0/0", config.getPeers().get(0).getAllowedIps(), expectedAllowedIps);
assertEquals("Test config has one DNS server", 1, config.getInterface().getDnsServers().size());
}
@Test(expected = BadConfigException.class)
public void invalid_config_throws() throws IOException, BadConfigException {
try (final InputStream is = Objects.requireNonNull(getClass().getClassLoader()).getResourceAsStream("broken.conf")) {
Config.parse(is);
}
}
}

View File

@ -0,0 +1,9 @@
[Interface]
PrivateKey = l0lth1s1sd3f1n1t3lybr0k3n=
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
[Peer]
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = 192.0.2.1:51820

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6Og=

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0L
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0,yes
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=

View File

@ -0,0 +1,8 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0

View File

@ -0,0 +1,5 @@
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint =
PersistentKeepalive = 0
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
DontLetTheFeelingFade = 1
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peers]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=

View File

@ -0,0 +1,9 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
PersistentKeepalive = 0
PublicKey = vBN7qyUTb5lJtWYJ8LhbPio1Z4RcyBPGnqFBGn6O6Qg=