/* * tlsdated-unittest.c - tlsdated unit tests * Copyright (c) 2012 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "config.h" #include "src/test_harness.h" #include "src/tlsdate.h" #include "src/util.h" #include #include #include #include #include #include FIXTURE (tempdir) { char path[PATH_MAX]; }; FIXTURE_SETUP (tempdir) { char *p; strncpy (self->path, "/tmp/tlsdated-unit-XXXXXX", sizeof (self->path)); p = mkdtemp (self->path); ASSERT_NE (NULL, p); } FIXTURE_TEARDOWN(tempdir) { char buf[256]; snprintf(buf, sizeof(buf), "%s/load", self->path); unlink(buf); snprintf(buf, sizeof(buf), "%s/save", self->path); unlink(buf); ASSERT_EQ(0, rmdir(self->path)); } int write_time (const char *path, time_t time) { int fd = open (path, O_WRONLY | O_TRUNC | O_CREAT, 0600); if (fd == -1) return 1; if (save_timestamp_to_fd (fd, time)) return 1; if (write (fd, &time, sizeof (time)) != sizeof (time)) { close (fd); return 1; } return close (fd); } int read_time (const char *path, time_t* time) { int fd = open (path, O_RDONLY); if (fd == -1) return 1; if (read (fd, time, sizeof (*time)) != sizeof (*time)) { close (fd); return 1; } return close (fd); } TEST (sane_time) { ASSERT_EQ (0, is_sane_time (0)); ASSERT_EQ (0, is_sane_time (INT_MAX)); } TEST (sane_host_time) { ASSERT_EQ (1, is_sane_time (time (NULL))); } TEST_F (tempdir, load_time) { char buf[PATH_MAX]; time_t tm = 3; time_t now = time (NULL); snprintf (buf, sizeof (buf), "%s/load", self->path); ASSERT_EQ (0, write_time (buf, 0)); ASSERT_EQ (-1, load_disk_timestamp (buf, &tm)); ASSERT_EQ (3, tm); ASSERT_EQ (0, write_time (buf, INT_MAX)); ASSERT_EQ (-1, load_disk_timestamp (buf, &tm)); ASSERT_EQ (3, tm); ASSERT_EQ (0, write_time (buf, now)); ASSERT_EQ (0, truncate (buf, 2)); ASSERT_EQ (-1, load_disk_timestamp (buf, &tm)); ASSERT_EQ (3, tm); ASSERT_EQ (0, unlink (buf)); ASSERT_EQ (-1, load_disk_timestamp (buf, &tm)); ASSERT_EQ (3, tm); ASSERT_EQ (0, write_time (buf, now)); ASSERT_EQ (0, load_disk_timestamp (buf, &tm)); ASSERT_EQ (now, tm); } TEST_F (tempdir, save_time) { char buf[PATH_MAX]; time_t now = time (NULL); time_t tm; snprintf (buf, sizeof (buf), "%s/save", self->path); ASSERT_EQ (0, write_time (buf, now)); ASSERT_EQ (0, read_time (buf, &tm)); EXPECT_EQ (now, tm); } FIXTURE (tlsdate) { struct state state; struct timeval timeout; }; FIXTURE_SETUP (tlsdate) { memset (self, 0, sizeof (*self)); /* TODO(wad) make this use the same function tlsdated uses. */ self->state.base = event_base_new(); set_conf_defaults (&self->state.opts); ASSERT_NE (NULL, self->state.base); event_base_priority_init (self->state.base, MAX_EVENT_PRIORITIES); ASSERT_EQ (0, setup_sigchld_event (&self->state, 1)); self->state.events[E_TLSDATE] = event_new (self->state.base, -1, EV_TIMEOUT, action_run_tlsdate, &self->state); ASSERT_NE (NULL, self->state.events[E_TLSDATE]); event_priority_set (self->state.events[E_TLSDATE], PRI_NET); /* The timeout and fd will be filled in per-call. */ ASSERT_EQ (0, setup_tlsdate_status (&self->state)); self->timeout.tv_sec = 1; } FIXTURE_TEARDOWN (tlsdate) { int i; for (i = 0; i < E_MAX; ++i) { struct event *e = self->state.events[i]; if (e) { int fd = event_get_fd (e); if (fd >= 0 && ! (event_get_events (e) & EV_SIGNAL)) close (fd); event_free (e); self->state.events[i] = NULL; } } /* The other half was closed above. */ close (self->state.tlsdate_monitor_fd); if (self->state.tlsdate_pid) { kill (self->state.tlsdate_pid, SIGKILL); waitpid (self->state.tlsdate_pid, NULL, WNOHANG); } if (self->state.base) event_base_free (self->state.base); } static int runner (FIXTURE_DATA (tlsdate) *self, time_t *newtime) { if (newtime) *newtime = 0; trigger_event (&self->state, E_TLSDATE, 0); event_base_loopexit (self->state.base, &self->timeout); if (event_base_dispatch (self->state.base)) return -1; if (self->state.last_time) { if (newtime) *newtime = self->state.last_time; return 0; } return 1; } TEST_F (tlsdate, runner_multi) { struct source source = { .next = NULL, .host = "host1", .port = "port1", .proxy = "proxy1" }; char *args[] = { "/nonexistent", NULL, NULL }; extern char **environ; self->state.opts.sources = &source; self->state.opts.base_argv = args; self->state.opts.subprocess_tries = 2; self->state.opts.subprocess_wait_between_tries = 1; self->state.opts.max_tries = 3; self->state.envp = environ; EXPECT_EQ (1, runner (self, NULL)); args[0] = "/bin/false"; self->state.tries = 0; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (1, runner (self, NULL)); args[0] = "src/test/check-host-1"; self->state.tries = 0; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); args[0] = "src/test/sleep-wrap"; args[1] = "3"; self->state.tries = 0; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); } TEST (jitter) { int i = 0; int r; const int kBase = 100; const int kJitter = 25; int nonequal = 0; for (i = 0; i < 1000; i++) { r = add_jitter (kBase, kJitter); EXPECT_GE (r, kBase - kJitter); EXPECT_LE (r, kBase + kJitter); if (r != kBase) nonequal++; } EXPECT_NE (nonequal, 0); } TEST_F (tlsdate, rotate_hosts) { struct source s2 = { .next = NULL, .host = "host2", .port = "port2", .proxy = "proxy2" }; struct source s1 = { .next = &s2, .host = "host1", .port = "port1", .proxy = "proxy1" }; char *args[] = { "src/test/check-host-1", NULL }; extern char **environ; self->state.envp = environ; self->state.opts.sources = &s1; self->state.opts.base_argv = args; self->state.opts.subprocess_tries = 2; self->state.opts.subprocess_wait_between_tries = 1; self->state.opts.max_tries = 5; self->timeout.tv_sec = 2; EXPECT_EQ (0, runner (self, NULL)); self->state.tries = 0; args[0] = "src/test/check-host-2"; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); self->state.tries = 0; args[0] = "src/test/check-host-1"; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); self->state.tries = 0; args[0] = "src/test/check-host-2"; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); } TEST_F (tlsdate, proxy_override) { struct source s1 = { .next = NULL, .host = "host", .port = "port", .proxy = NULL, }; char *args[] = { "src/test/proxy-override", NULL }; extern char **environ; self->state.envp = environ; self->state.opts.sources = &s1; self->state.opts.base_argv = args; self->state.opts.subprocess_tries = 2; self->state.opts.subprocess_wait_between_tries = 1; EXPECT_EQ (0, runner (self, NULL)); EXPECT_EQ (RECENT_COMPILE_DATE + 1, self->state.last_time); s1.proxy = "socks5://bad.proxy"; self->state.tries = 0; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); EXPECT_EQ (RECENT_COMPILE_DATE + 3, self->state.last_time); self->state.opts.proxy = "socks5://good.proxy"; self->state.tries = 0; self->state.last_sync_type = SYNC_TYPE_NONE; EXPECT_EQ (0, runner (self, NULL)); EXPECT_EQ (RECENT_COMPILE_DATE + 2, self->state.last_time); } FIXTURE(mock_platform) { struct platform platform; struct platform *old_platform; }; FIXTURE_SETUP(mock_platform) { self->old_platform = platform; self->platform.rtc_open = NULL; self->platform.rtc_write = NULL; self->platform.rtc_read = NULL; self->platform.rtc_close = NULL; platform = &self->platform; } FIXTURE_TEARDOWN(mock_platform) { platform = self->old_platform; } /* TODO: leap_tests, time_setter tests. */ TEST_HARNESS_MAIN