blob: 67a4f44a9863f70b2ee3d5955a162e9c19fb5873 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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 android.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.net.Network;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NtpTrustedTimeTest {
// Valid absolute URIs, but not ones that will be accepted as NTP server URIs.
private static final List<String> BAD_ABSOLUTE_NTP_URIS = Arrays.asList(
"ntp://:123/",
"ntp://:123",
"ntp://:/",
"ntp://:",
"ntp://foobar:abc/",
"ntp://foobar:abc",
"ntp://foobar:456:789/",
"ntp://foobar:456:789",
"ntp://foobar:456:abc/",
"ntp://foobar:456:abc"
);
// Valid relative URIs, but not ones that will be accepted as NTP server URIs.
private static final List<String> BAD_RELATIVE_NTP_URIS = Arrays.asList(
"foobar",
"/foobar",
"foobar:456"
);
// Valid NTP server URIs: input value -> expected URI.toString() value.
private static final Map<String, String> GOOD_NTP_URIS = Map.of(
"ntp://foobar", "ntp://foobar",
"ntp://foobar/", "ntp://foobar/",
"ntp://foobar:456/", "ntp://foobar:456/",
"ntp://foobar:456", "ntp://foobar:456"
);
private static final URI VALID_SERVER_URI = URI.create("ntp://foobar/");
@Test
public void testParseNtpServerSetting() {
assertEquals(URI.create("ntp://foobar"), NtpTrustedTime.parseNtpServerSetting("foobar"));
// Legacy settings values which could easily be confused with relative URIs. Parsing of this
// legacy form doesn't have to be robust / treated as errors: Android has never supported
// string like these, and so they won't work properly.
assertNull(NtpTrustedTime.parseNtpServerSetting("foobar:123"));
assertNull(NtpTrustedTime.parseNtpServerSetting("/foobar"));
// NTP URI cases that must not be accepted.
for (String badNtpUri : BAD_ABSOLUTE_NTP_URIS) {
assertNull("Input: \"" + badNtpUri + "\"",
NtpTrustedTime.parseNtpServerSetting(badNtpUri));
}
// Valid URIs
for (Map.Entry<String, String> goodNtpUri : GOOD_NTP_URIS.entrySet()) {
URI uri = NtpTrustedTime.parseNtpServerSetting(goodNtpUri.getKey());
assertNotNull(goodNtpUri.getKey(), uri);
assertEquals(goodNtpUri.getValue(), uri.toString());
}
}
@Test
public void testParseNtpUriStrict() throws Exception {
// ntp: URI cases that must not be accepted.
for (String badNtpUri : BAD_ABSOLUTE_NTP_URIS) {
assertParseNtpUriStrictThrows(badNtpUri);
}
for (String badNtpUri : BAD_RELATIVE_NTP_URIS) {
assertParseNtpUriStrictThrows(badNtpUri);
}
// Bad scheme.
assertParseNtpUriStrictThrows("notntp://foobar:123");
// Valid NTP URIs
for (Map.Entry<String, String> goodNtpUri : GOOD_NTP_URIS.entrySet()) {
URI uri = NtpTrustedTime.parseNtpUriStrict(goodNtpUri.getKey());
assertNotNull(goodNtpUri.getKey(), uri);
assertEquals(goodNtpUri.getValue(), uri.toString());
}
}
private void assertParseNtpUriStrictThrows(String badNtpUri) throws Exception {
assertThrows("Input: \"" + badNtpUri, URISyntaxException.class,
() -> NtpTrustedTime.parseNtpUriStrict(badNtpUri));
}
@Test(expected = NullPointerException.class)
public void testNtpConfig_nullConstructorServerInfo() {
new NtpTrustedTime.NtpConfig(null, Duration.ofSeconds(5));
}
@Test(expected = NullPointerException.class)
public void testNtpConfig_nullConstructorTimeout() {
new NtpTrustedTime.NtpConfig(VALID_SERVER_URI, null);
}
@Test(expected = IllegalArgumentException.class)
public void testNtpConfig_zeroTimeout() {
new NtpTrustedTime.NtpConfig(VALID_SERVER_URI, Duration.ofMillis(0));
}
@Test(expected = IllegalArgumentException.class)
public void testNtpConfig_negativeTimeout() {
new NtpTrustedTime.NtpConfig(VALID_SERVER_URI, Duration.ofMillis(-1));
}
@Test
public void testForceRefresh_nullConfig() {
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(null);
assertFalse(ntpTrustedTime.forceRefresh());
assertFalse(ntpTrustedTime.hasCache());
assertEquals(0, ntpTrustedTime.getCachedNtpTime());
assertEquals(0, ntpTrustedTime.getCachedNtpTimeReference());
assertEquals(Long.MAX_VALUE, ntpTrustedTime.getCacheAge());
assertNull(ntpTrustedTime.getCachedTimeResult());
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
verify(ntpTrustedTime, never()).getNetwork();
verify(ntpTrustedTime, never()).queryNtpServer(any(), any(), any());
}
@Test
public void testForceRefresh_noConnectivity() {
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
URI serverUri = URI.create("ntp://ntpserver.name");
Duration timeout = Duration.ofSeconds(5);
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
new NtpTrustedTime.NtpConfig(serverUri, timeout));
when(ntpTrustedTime.getNetwork()).thenReturn(null);
assertFalse(ntpTrustedTime.forceRefresh());
assertFalse(ntpTrustedTime.hasCache());
assertEquals(0, ntpTrustedTime.getCachedNtpTime());
assertEquals(0, ntpTrustedTime.getCachedNtpTimeReference());
assertEquals(Long.MAX_VALUE, ntpTrustedTime.getCacheAge());
assertNull(ntpTrustedTime.getCachedTimeResult());
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
verify(ntpTrustedTime, times(1)).getNetwork();
verify(ntpTrustedTime, never()).queryNtpServer(any(), any(), any());
}
@Test
public void testForceRefresh_queryFailed() {
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
URI serverUri = URI.create("ntp://ntpserver.name");
Duration timeout = Duration.ofSeconds(5);
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
new NtpTrustedTime.NtpConfig(serverUri, timeout));
Network network = mock(Network.class);
when(ntpTrustedTime.getNetwork()).thenReturn(network);
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(null);
assertFalse(ntpTrustedTime.forceRefresh());
assertFalse(ntpTrustedTime.hasCache());
assertEquals(0, ntpTrustedTime.getCachedNtpTime());
assertEquals(0, ntpTrustedTime.getCachedNtpTimeReference());
assertEquals(Long.MAX_VALUE, ntpTrustedTime.getCacheAge());
assertNull(ntpTrustedTime.getCachedTimeResult());
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
verify(ntpTrustedTime, times(1)).getNetwork();
verify(ntpTrustedTime, times(1)).queryNtpServer(network, serverUri, timeout);
}
@Test
public void testForceRefresh_querySucceeded() {
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
URI serverUri = URI.create("ntp://ntpserver.name");
Duration timeout = Duration.ofSeconds(5);
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
new NtpTrustedTime.NtpConfig(serverUri, timeout));
Network network = mock(Network.class);
when(ntpTrustedTime.getNetwork()).thenReturn(network);
NtpTrustedTime.TimeResult successResult = new NtpTrustedTime.TimeResult(123L, 456L, 789,
InetSocketAddress.createUnresolved("placeholder", 123));
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult);
assertTrue(ntpTrustedTime.forceRefresh());
assertTrue(ntpTrustedTime.hasCache());
assertEquals(successResult.getTimeMillis(), ntpTrustedTime.getCachedNtpTime());
assertEquals(successResult.getElapsedRealtimeMillis(),
ntpTrustedTime.getCachedNtpTimeReference());
assertTrue(ntpTrustedTime.getCacheAge() != Long.MAX_VALUE);
assertEquals(successResult, ntpTrustedTime.getCachedTimeResult());
verify(ntpTrustedTime, times(1)).getNtpConfigInternal();
verify(ntpTrustedTime, times(1)).getNetwork();
verify(ntpTrustedTime, times(1)).queryNtpServer(network, serverUri, timeout);
}
@Test
public void testForceRefresh_keepsOldValueOnFailure() {
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
URI serverUri = URI.create("ntp://ntpserver.name");
Duration timeout = Duration.ofSeconds(5);
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
new NtpTrustedTime.NtpConfig(serverUri, timeout));
Network network = mock(Network.class);
when(ntpTrustedTime.getNetwork()).thenReturn(network);
NtpTrustedTime.TimeResult successResult = new NtpTrustedTime.TimeResult(123L, 456L, 789,
InetSocketAddress.createUnresolved("placeholder", 123));
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult);
assertTrue(ntpTrustedTime.forceRefresh());
assertTrue(ntpTrustedTime.hasCache());
assertEquals(successResult, ntpTrustedTime.getCachedTimeResult());
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(null);
assertFalse(ntpTrustedTime.forceRefresh());
assertTrue(ntpTrustedTime.hasCache());
assertEquals(successResult, ntpTrustedTime.getCachedTimeResult());
}
@Test
public void testForceRefresh_keepsNewValueOnSuccess() {
NtpTrustedTime ntpTrustedTime = spy(NtpTrustedTime.class);
URI serverUri = URI.create("ntp://ntpserver.name");
Duration timeout = Duration.ofSeconds(5);
when(ntpTrustedTime.getNtpConfigInternal()).thenReturn(
new NtpTrustedTime.NtpConfig(serverUri, timeout));
Network network = mock(Network.class);
when(ntpTrustedTime.getNetwork()).thenReturn(network);
NtpTrustedTime.TimeResult successResult1 = new NtpTrustedTime.TimeResult(123L, 456L, 789,
InetSocketAddress.createUnresolved("placeholder", 123));
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult1);
assertTrue(ntpTrustedTime.forceRefresh());
assertTrue(ntpTrustedTime.hasCache());
assertEquals(successResult1, ntpTrustedTime.getCachedTimeResult());
NtpTrustedTime.TimeResult successResult2 = new NtpTrustedTime.TimeResult(123L, 456L, 789,
InetSocketAddress.createUnresolved("placeholder", 123));
when(ntpTrustedTime.queryNtpServer(network, serverUri, timeout)).thenReturn(successResult2);
assertTrue(ntpTrustedTime.forceRefresh());
assertTrue(ntpTrustedTime.hasCache());
assertEquals(successResult2, ntpTrustedTime.getCachedTimeResult());
}
}