diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/TestContext.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/MockContext.java similarity index 71% rename from src/androidTest/java/com/nutomic/syncthingandroid/test/TestContext.java rename to src/androidTest/java/com/nutomic/syncthingandroid/test/MockContext.java index fadc7859..97c2acd5 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/TestContext.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/MockContext.java @@ -3,11 +3,8 @@ package com.nutomic.syncthingandroid.test; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; -import android.content.res.Resources; -import android.test.mock.MockContext; - -import junit.framework.Test; import java.io.File; import java.util.ArrayList; @@ -16,9 +13,7 @@ import java.util.List; /** * Context that saves all received intents, which can be retrieved later by test classes. */ -public class TestContext extends MockContext { - - private Context mContext; +public class MockContext extends ContextWrapper { private ArrayList mReceivedIntents = new ArrayList<>(); @@ -26,8 +21,8 @@ public class TestContext extends MockContext { * Use the actual context for calls that aren't easily mocked. May be null if those * calls aren't needed. */ - public TestContext(Context context) { - mContext = context; + public MockContext(Context context) { + super(context); } @Override @@ -35,11 +30,6 @@ public class TestContext extends MockContext { return null; } - @Override - public Object getSystemService(String name) { - return mContext.getSystemService(name); - } - @Override public ComponentName startService(Intent intent) { mReceivedIntents.add(intent); @@ -67,13 +57,9 @@ public class TestContext extends MockContext { @Override public File getFilesDir() { - File testFilesDir = new File(mContext.getFilesDir(), "test/"); + File testFilesDir = new File(super.getFilesDir(), "test/"); testFilesDir.mkdir(); return testFilesDir; } - @Override - public Resources getResources() { - return mContext.getResources(); - } } diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/MockRestApi.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/MockRestApi.java new file mode 100644 index 00000000..4addb0d2 --- /dev/null +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/MockRestApi.java @@ -0,0 +1,102 @@ +package com.nutomic.syncthingandroid.test; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; + +import com.nutomic.syncthingandroid.syncthing.RestApi; + +import java.util.List; + +public class MockRestApi extends RestApi { + + public MockRestApi(Context context, String url, OnApiAvailableListener listener) { + super(context, url, listener); + } + + @Override + public void setApiKey(String apiKey) { + throw new UnsupportedOperationException(); + } + + @Override + public void onWebGuiAvailable() { + throw new UnsupportedOperationException(); + } + + @Override + public String getVersion() { + throw new UnsupportedOperationException(); + } + + @Override + public void shutdown() { + throw new UnsupportedOperationException(); + } + + @Override + public String getValue(String name, String key) { + return ""; + } + + @Override + public void setValue(String name, String key, T value, boolean isArray, Activity activity) { + throw new UnsupportedOperationException(); + } + + @Override + public List getNodes() { + throw new UnsupportedOperationException(); + } + + @Override + public void getSystemInfo(final OnReceiveSystemInfoListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public List getRepos() { + throw new UnsupportedOperationException(); + } + + @Override + public void getConnections(final OnReceiveConnectionsListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void getModel(final String repoId, final OnReceiveModelListener listener) { + } + + @Override + public void editNode(final Node node, + final OnNodeIdNormalizedListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean deleteNode(Node node, Context context) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean editRepo(Repo repo, boolean create, Context context) { + throw new UnsupportedOperationException(); + } + + public boolean deleteRepo(Repo repo, Context context) { + throw new UnsupportedOperationException(); + } + + @Override + public void normalizeNodeId(String id, final OnNodeIdNormalizedListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + @TargetApi(11) + public void copyNodeId(String id) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/MockSyncthingService.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/MockSyncthingService.java index 43fb322b..9d44d6bc 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/MockSyncthingService.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/MockSyncthingService.java @@ -1,6 +1,5 @@ package com.nutomic.syncthingandroid.test; -import android.app.Activity; import android.content.Intent; import android.os.IBinder; @@ -8,7 +7,6 @@ import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.SyncthingService; import java.util.LinkedList; -import java.util.List; public class MockSyncthingService extends SyncthingService { @@ -51,7 +49,7 @@ public class MockSyncthingService extends SyncthingService { @Override public RestApi getApi() { - throw new UnsupportedOperationException(); + return new MockRestApi(this, null, null); } @Override diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java index 81e62961..66e6b39c 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BatteryReceiverTest.java @@ -7,18 +7,18 @@ import android.test.suitebuilder.annotation.MediumTest; import com.nutomic.syncthingandroid.syncthing.BatteryReceiver; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; import com.nutomic.syncthingandroid.syncthing.SyncthingService; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; public class BatteryReceiverTest extends AndroidTestCase { private BatteryReceiver mReceiver; - private TestContext mContext; + private MockContext mContext; @Override protected void setUp() throws Exception { super.setUp(); mReceiver = new BatteryReceiver(); - mContext = new TestContext(null); + mContext = new MockContext(null); } @MediumTest diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java index db2e60cb..5e756bc7 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/BootReceiverTest.java @@ -6,7 +6,7 @@ import android.test.suitebuilder.annotation.MediumTest; import com.nutomic.syncthingandroid.syncthing.BootReceiver; import com.nutomic.syncthingandroid.syncthing.SyncthingService; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; /** * Tests that {@link com.nutomic.syncthingandroid.syncthing.BootReceiver} starts the right service @@ -15,13 +15,13 @@ import com.nutomic.syncthingandroid.test.TestContext; public class BootReceiverTest extends AndroidTestCase { private BootReceiver mReceiver; - private TestContext mContext; + private MockContext mContext; @Override protected void setUp() throws Exception { super.setUp(); mReceiver = new BootReceiver(); - mContext = new TestContext(null); + mContext = new MockContext(null); } @MediumTest diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/DeviceStateHolderTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/DeviceStateHolderTest.java index 13250d6c..470fcbf7 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/DeviceStateHolderTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/DeviceStateHolderTest.java @@ -4,21 +4,20 @@ import android.content.Intent; import android.os.BatteryManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; public class DeviceStateHolderTest extends AndroidTestCase { private DeviceStateHolder mReceiver; - private TestContext mContext; + private MockContext mContext; @Override protected void setUp() throws Exception { super.setUp(); mReceiver = new DeviceStateHolder(getContext()); - mContext = new TestContext(null); + mContext = new MockContext(null); } @MediumTest diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/GetTaskTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/GetTaskTest.java index 9e7f06cc..86e86a8d 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/GetTaskTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/GetTaskTest.java @@ -3,7 +3,6 @@ package com.nutomic.syncthingandroid.test.syncthing; import android.net.Uri; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; -import android.util.Log; import com.nutomic.syncthingandroid.syncthing.GetTask; import com.nutomic.syncthingandroid.syncthing.RestApi; diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java index bbbdd07a..0a2c8138 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/NetworkReceiverTest.java @@ -4,10 +4,10 @@ import android.content.Intent; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; -import com.nutomic.syncthingandroid.syncthing.NetworkReceiver; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; +import com.nutomic.syncthingandroid.syncthing.NetworkReceiver; import com.nutomic.syncthingandroid.syncthing.SyncthingService; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; /** * Tests for correct extras on the Intent sent by @@ -19,13 +19,13 @@ import com.nutomic.syncthingandroid.test.TestContext; public class NetworkReceiverTest extends AndroidTestCase { private NetworkReceiver mReceiver; - private TestContext mContext; + private MockContext mContext; @Override protected void setUp() throws Exception { super.setUp(); mReceiver = new NetworkReceiver(); - mContext = new TestContext(getContext()); + mContext = new MockContext(getContext()); } @MediumTest diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PollWebGuiAvailableTaskTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PollWebGuiAvailableTaskTest.java index eb5d3a70..92ab36ed 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PollWebGuiAvailableTaskTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PollWebGuiAvailableTaskTest.java @@ -4,10 +4,9 @@ import android.test.AndroidTestCase; import com.nutomic.syncthingandroid.syncthing.PollWebGuiAvailableTask; import com.nutomic.syncthingandroid.syncthing.PostTask; -import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; import com.nutomic.syncthingandroid.syncthing.SyncthingService; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; import com.nutomic.syncthingandroid.util.ConfigXml; import java.util.concurrent.CountDownLatch; @@ -23,7 +22,7 @@ public class PollWebGuiAvailableTaskTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mConfig = new ConfigXml(new TestContext(getContext())); + mConfig = new ConfigXml(new MockContext(getContext())); mConfig.updateIfNeeded(); } @@ -31,11 +30,11 @@ public class PollWebGuiAvailableTaskTest extends AndroidTestCase { protected void tearDown() throws Exception { super.tearDown(); - ConfigXml.getConfigFile(new TestContext(getContext())).delete(); + ConfigXml.getConfigFile(new MockContext(getContext())).delete(); } public void testPolling() throws InterruptedException { - mSyncthing = new SyncthingRunnable(new TestContext(null), + mSyncthing = new SyncthingRunnable(new MockContext(null), getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME); final CountDownLatch latch = new CountDownLatch(1); diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PostTaskTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PostTaskTest.java index 85f21a10..0c6b7af0 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PostTaskTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/PostTaskTest.java @@ -3,7 +3,6 @@ package com.nutomic.syncthingandroid.test.syncthing; import android.net.Uri; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; -import android.util.Log; import com.nutomic.syncthingandroid.syncthing.GetTask; import com.nutomic.syncthingandroid.syncthing.PostTask; diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/RestApiTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/RestApiTest.java index 538f080e..2f464075 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/RestApiTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/RestApiTest.java @@ -10,7 +10,7 @@ import com.nutomic.syncthingandroid.syncthing.PostTask; import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; import com.nutomic.syncthingandroid.syncthing.SyncthingService; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; import com.nutomic.syncthingandroid.util.ConfigXml; import java.util.ArrayList; @@ -30,10 +30,10 @@ public class RestApiTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mSyncthing = new SyncthingRunnable(new TestContext(null), + mSyncthing = new SyncthingRunnable(new MockContext(null), getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME); - mConfig = new ConfigXml(new TestContext(getContext())); + mConfig = new ConfigXml(new MockContext(getContext())); mConfig.createCameraRepo(); mConfig.updateIfNeeded(); @@ -69,7 +69,7 @@ public class RestApiTest extends AndroidTestCase { } }.execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN, mSyncthing.getApiKey()); latch.await(1, TimeUnit.SECONDS); - ConfigXml.getConfigFile(new TestContext(getContext())).delete(); + ConfigXml.getConfigFile(new MockContext(getContext())).delete(); } @SmallTest diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java index 9cd49bff..c7edc1f6 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingRunnableTest.java @@ -4,7 +4,7 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; import java.io.File; @@ -12,7 +12,7 @@ public class SyncthingRunnableTest extends AndroidTestCase { @SmallTest public void testRunning() throws InterruptedException { - TestContext context = new TestContext(getContext()); + MockContext context = new MockContext(getContext()); File testFile = new File(context.getFilesDir(), "was_running"); assertFalse(testFile.exists()); // Inject a differenct command instead of the syncthing binary for testing. @@ -23,7 +23,7 @@ public class SyncthingRunnableTest extends AndroidTestCase { @SmallTest public void testApiKey() { - SyncthingRunnable st = new SyncthingRunnable(new TestContext(getContext()), "ls\n"); + SyncthingRunnable st = new SyncthingRunnable(new MockContext(getContext()), "ls\n"); assertEquals(20, st.getApiKey().length()); } diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java new file mode 100644 index 00000000..000996f7 --- /dev/null +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/SyncthingServiceTest.java @@ -0,0 +1,195 @@ +package com.nutomic.syncthingandroid.test.syncthing; + +import android.content.Context; +import android.content.Intent; +import android.preference.PreferenceManager; +import android.test.ServiceTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; + +import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; +import com.nutomic.syncthingandroid.syncthing.SyncthingService; +import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder; +import com.nutomic.syncthingandroid.test.MockContext; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * FIXME: There are some problems with shutting down the service after tests. It may be that the + * service remains running after short tests. As a workaround, kill the app in Android. + * NOTE: It seems that @link #tearDown()} is not executed if a test fails, so the test data folder + * is not deleted (which may cause following tests to fail). + */ +public class SyncthingServiceTest extends ServiceTestCase { + + private Context mContext; + + public SyncthingServiceTest() { + super(SyncthingService.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContext = new MockContext(getContext()); + setContext(mContext); + } + + @Override + protected void tearDown() throws Exception { + deleteRecursive(getContext().getFilesDir()); + PreferenceManager.getDefaultSharedPreferences(getContext()).edit().clear().commit(); + + super.tearDown(); + } + + private void deleteRecursive(File file) { + if (file.isDirectory()) { + for (File f : file.listFiles()) { + deleteRecursive(f); + } + } + file.delete(); + } + + @LargeTest + public void testStartService() throws InterruptedException { + startService(new Intent(mContext, SyncthingService.class)); + final CountDownLatch latch = new CountDownLatch(2); + getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() { + @Override + public void onWebGuiAvailable() { + latch.countDown(); + } + }); + getService().registerOnApiChangeListener(new SyncthingService.OnApiChangeListener() { + @Override + public void onApiChange(SyncthingService.State currentState) { + latch.countDown(); + } + }); + latch.await(1, TimeUnit.SECONDS); + assertNotNull(getService().getApi()); + assertNotNull(getService().getWebGuiUrl()); + } + + @SmallTest + public void testFirstStart() { + startService(new Intent(mContext, SyncthingService.class)); + assertTrue(getService().isFirstStart()); + } + + @MediumTest + public void testNotFirstStart() throws IOException { + startService(new Intent(mContext, SyncthingService.class)); + new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE).createNewFile(); + assertFalse(getService().isFirstStart()); + } + + @SmallTest + public void testBindService() throws InterruptedException { + SyncthingServiceBinder binder = (SyncthingServiceBinder) + bindService(new Intent(mContext, SyncthingService.class)); + SyncthingService service = binder.getService(); + final CountDownLatch latch = new CountDownLatch(2); + getService().registerOnWebGuiAvailableListener(new SyncthingService.OnWebGuiAvailableListener() { + @Override + public void onWebGuiAvailable() { + latch.countDown(); + } + }); + getService().registerOnApiChangeListener(new SyncthingService.OnApiChangeListener() { + @Override + public void onApiChange(SyncthingService.State currentState) { + latch.countDown(); + } + }); + latch.await(1, TimeUnit.SECONDS); + assertNotNull(service); + } + + private class Listener implements SyncthingService.OnApiChangeListener { + + private SyncthingService.State mLastState; + + @Override + public void onApiChange(SyncthingService.State currentState) { + mLastState = currentState; + } + + public SyncthingService.State getLastState() { + return mLastState; + } + + } + + private Listener mListener = new Listener(); + + @MediumTest + public void testStatesAllRequired() throws InterruptedException { + setupStatesTest(true, true); + + assertState(true, true, SyncthingService.State.ACTIVE); + assertState(true, false, SyncthingService.State.DISABLED); + assertState(false, true, SyncthingService.State.DISABLED); + assertState(false, false, SyncthingService.State.DISABLED); + } + + @MediumTest + public void testStatesWifiRequired() throws InterruptedException { + setupStatesTest(true, false); + + assertState(true, true, SyncthingService.State.ACTIVE); + assertState(true, false, SyncthingService.State.DISABLED); + assertState(false, true, SyncthingService.State.ACTIVE); + assertState(false, false, SyncthingService.State.DISABLED); + } + + @MediumTest + public void testStatesChargingRequired() throws InterruptedException { + setupStatesTest(false, true); + + assertState(true, true, SyncthingService.State.ACTIVE); + assertState(true, false, SyncthingService.State.ACTIVE); + assertState(false, true, SyncthingService.State.DISABLED); + assertState(false, false, SyncthingService.State.DISABLED); + } + + @MediumTest + public void testStatesNoneRequired() throws InterruptedException { + setupStatesTest(false, false); + + assertState(true, true, SyncthingService.State.ACTIVE); + assertState(true, false, SyncthingService.State.ACTIVE); + assertState(false, true, SyncthingService.State.ACTIVE); + assertState(false, false, SyncthingService.State.ACTIVE); + } + + public void assertState(boolean charging, boolean wifi, SyncthingService.State expected) + throws InterruptedException { + Intent i = new Intent(mContext, SyncthingService.class); + i.putExtra(DeviceStateHolder.EXTRA_IS_CHARGING, charging); + i.putExtra(DeviceStateHolder.EXTRA_HAS_WIFI, wifi); + startService(i); + // Wait for service to react to preference change. + Thread.sleep(7500); + assertEquals(expected, mListener.getLastState()); + } + + public void setupStatesTest(boolean syncOnlyWifi, boolean syncOnlyCharging) + throws InterruptedException { + PreferenceManager.getDefaultSharedPreferences(getContext()).edit() + .putBoolean(SyncthingService.PREF_SYNC_ONLY_WIFI, syncOnlyWifi) + .putBoolean(SyncthingService.PREF_SYNC_ONLY_CHARGING, syncOnlyCharging) + .commit(); + // Wait for service to react to preference change. + Thread.sleep(1000); + startService(new Intent(getContext(), SyncthingService.class)); + getService().registerOnApiChangeListener(mListener); + assertEquals(SyncthingService.State.INIT, mListener.getLastState()); + } +} diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ConfigXmlTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ConfigXmlTest.java index a6477997..84c90fa1 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ConfigXmlTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ConfigXmlTest.java @@ -1,21 +1,15 @@ package com.nutomic.syncthingandroid.test.util; -import android.content.Intent; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; -import com.nutomic.syncthingandroid.syncthing.RestApi; -import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; -import com.nutomic.syncthingandroid.syncthing.SyncthingService; -import com.nutomic.syncthingandroid.test.TestContext; +import com.nutomic.syncthingandroid.test.MockContext; import com.nutomic.syncthingandroid.util.ConfigXml; -import java.io.File; - public class ConfigXmlTest extends AndroidTestCase { - private TestContext mContext; + private MockContext mContext; private ConfigXml mConfig; @@ -23,7 +17,7 @@ public class ConfigXmlTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mContext = new TestContext(getContext()); + mContext = new MockContext(getContext()); assertFalse(ConfigXml.getConfigFile(mContext).exists()); mConfig = new ConfigXml(mContext); assertTrue(ConfigXml.getConfigFile(mContext).exists()); diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/util/NodesAdapterTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/util/NodesAdapterTest.java index c436001e..c074366c 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/util/NodesAdapterTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/util/NodesAdapterTest.java @@ -7,11 +7,8 @@ import android.widget.TextView; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.syncthing.RestApi; -import com.nutomic.syncthingandroid.test.TestContext; import com.nutomic.syncthingandroid.util.NodesAdapter; -import com.nutomic.syncthingandroid.util.ReposAdapter; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; diff --git a/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ReposAdapterTest.java b/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ReposAdapterTest.java index a7ef3ca7..3375b16c 100644 --- a/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ReposAdapterTest.java +++ b/src/androidTest/java/com/nutomic/syncthingandroid/test/util/ReposAdapterTest.java @@ -7,7 +7,6 @@ import android.widget.TextView; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.syncthing.RestApi; -import com.nutomic.syncthingandroid.test.TestContext; import com.nutomic.syncthingandroid.util.ReposAdapter; import java.util.ArrayList; diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java index 833c27df..9d993501 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java @@ -146,15 +146,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { private String mLocalNodeId; - private final NotificationManager mNotificationManager; - private boolean mRestartPostponed = false; /** * Stores the result of the last successful request to {@link GetTask#URI_CONNECTIONS}, * or an empty HashMap. */ - private HashMap mPreviousConnections = new HashMap(); + private HashMap mPreviousConnections = new HashMap<>(); /** * Stores the timestamp of the last successful request to {@link GetTask#URI_CONNECTIONS}. @@ -170,8 +168,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { public RestApi(Context context, String url, OnApiAvailableListener listener) { mContext = context; mUrl = url; - mNotificationManager = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); mOnApiAvailableListener = listener; } @@ -257,7 +253,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { * Stops syncthing. You should probably use SyncthingService.stopService() instead. */ public void shutdown() { - mNotificationManager.cancel(NOTIFICATION_RESTART); + // Happens in unit tests. + if (mContext == null) + return; + + NotificationManager nm = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(NOTIFICATION_RESTART); new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey); } @@ -371,7 +373,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener { .setContentIntent(pi) .build(); n.flags |= Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL; - mNotificationManager.notify(NOTIFICATION_RESTART, n); + NotificationManager nm = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(NOTIFICATION_RESTART, n); mRestartPostponed = true; } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java index f5ac40e5..ef62f213 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java @@ -2,8 +2,6 @@ package com.nutomic.syncthingandroid.syncthing; import android.app.Activity; import android.app.AlertDialog; -import android.app.Notification; -import android.app.PendingIntent; import android.app.Service; import android.content.DialogInterface; import android.content.Intent; @@ -12,25 +10,14 @@ import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.IBinder; import android.preference.PreferenceManager; -import android.support.v4.app.NotificationCompat; import android.util.Log; -import android.util.Pair; import com.nutomic.syncthingandroid.R; -import com.nutomic.syncthingandroid.activities.MainActivity; import com.nutomic.syncthingandroid.activities.SettingsActivity; import com.nutomic.syncthingandroid.util.ConfigXml; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.conn.HttpHostConnectException; -import org.apache.http.impl.client.DefaultHttpClient; - import java.io.File; import java.io.FilenameFilter; -import java.io.IOException; import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Iterator; @@ -42,8 +29,6 @@ public class SyncthingService extends Service { private static final String TAG = "SyncthingService"; - private static final int NOTIFICATION_RUNNING = 1; - /** * Intent action to perform a syncthing restart. */ @@ -57,7 +42,7 @@ public class SyncthingService extends Service { /** * Name of the public key file in the data directory. */ - private static final String PUBLIC_KEY_FILE = "cert.pem"; + public static final String PUBLIC_KEY_FILE = "cert.pem"; /** * Name of the private key file in the data directory. @@ -69,6 +54,10 @@ public class SyncthingService extends Service { */ public static final String BINARY_NAME = "lib/libsyncthing.so"; + public static final String PREF_SYNC_ONLY_WIFI = "stop_sync_on_mobile_data"; + + public static final String PREF_SYNC_ONLY_CHARGING = "stop_sync_while_not_charging"; + private ConfigXml mConfig; private RestApi mApi; @@ -143,14 +132,14 @@ public class SyncthingService extends Service { /** * Checks according to preferences and charging/wifi state, whether syncthing should be enabled * or not. - *

+ * * Depending on the result, syncthing is started or stopped, and {@link #onApiChange()} is * called. */ public void updateState() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean prefStopMobileData = prefs.getBoolean("stop_sync_on_mobile_data", true); - boolean prefStopNotCharging = prefs.getBoolean("stop_sync_while_not_charging", true); + boolean prefStopMobileData = prefs.getBoolean(PREF_SYNC_ONLY_WIFI, true); + boolean prefStopNotCharging = prefs.getBoolean(PREF_SYNC_ONLY_CHARGING, true); // Start syncthing. if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) && @@ -187,7 +176,7 @@ public class SyncthingService extends Service { /** * Move config file, keys, and index files to "official" folder - *

+ * * Intended to bring the file locations in older installs in line with * newer versions. */ @@ -221,18 +210,6 @@ public class SyncthingService extends Service { */ @Override public void onCreate() { - PendingIntent pi = PendingIntent.getActivity( - this, 0, new Intent(this, MainActivity.class), - PendingIntent.FLAG_UPDATE_CURRENT); - Notification n = new NotificationCompat.Builder(this) - .setContentTitle(getString(R.string.app_name)) - .setSmallIcon(R.drawable.ic_launcher) - .setContentIntent(pi) - .setPriority(NotificationCompat.PRIORITY_MIN) - .build(); - n.flags |= Notification.FLAG_ONGOING_EVENT; - startForeground(NOTIFICATION_RUNNING, n); - mDeviceStateHolder = new DeviceStateHolder(SyncthingService.this); registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); new StartupTask().execute(); @@ -240,8 +217,7 @@ public class SyncthingService extends Service { /** * Sets up the initial configuration, updates the config when coming from an old - * version, and reads syncthing URL and API key (these are passed internally as - * {@code Pair}. TODO + * version, and reads syncthing URL.. */ private class StartupTask extends AsyncTask { @Override @@ -272,7 +248,7 @@ public class SyncthingService extends Service { Log.i(TAG, "Web GUI will be available at " + mConfig.getWebGuiUrl()); // HACK: Make sure there is no syncthing binary left running from an improper - // shutdown (eg Play Store updateIfNeeded). + // shutdown (eg Play Store update). // NOTE: This will log an exception if syncthing is not actually running. mApi.shutdown(); updateState(); @@ -291,12 +267,14 @@ public class SyncthingService extends Service { public void onDestroy() { super.onDestroy(); Log.i(TAG, "Shutting down service"); - mApi.shutdown(); + if (mApi != null) { + mApi.shutdown(); + } } /** * Register a listener for the web gui becoming available.. - *

+ * * If the web gui is already available, listener will be called immediately. * Listeners are unregistered automatically after being called. */ @@ -310,7 +288,7 @@ public class SyncthingService extends Service { /** * Returns true if this service has not been started before (ie config.xml does not exist). - *

+ * * This will return true until the public key file has been generated. */ public boolean isFirstStart() { @@ -323,7 +301,7 @@ public class SyncthingService extends Service { /** * Register a listener for the syncthing API state changing. - *

+ * * The listener is called immediately with the current state, and again whenever the state * changes. */ @@ -331,7 +309,7 @@ public class SyncthingService extends Service { // Make sure we don't send an invalid state or syncthing might shwow a "disabled" message // when it's just starting up. listener.onApiChange(mCurrentState); - mOnApiChangeListeners.add(new WeakReference(listener)); + mOnApiChangeListeners.add(new WeakReference<>(listener)); } private class PollWebGuiAvailableTaskImpl extends PollWebGuiAvailableTask { diff --git a/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java b/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java index bb4354c0..fe1af90a 100644 --- a/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java +++ b/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java @@ -15,7 +15,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Random; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory;