1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-11-30 08:11:17 +00:00

Various test enhancements:

- added SyncthingServiceTest
- removed permanent notification from SyncthingService (didn't work with unit test)
- renamed TestContext to MockContext
- added MockRestApi
This commit is contained in:
Felix Ableitner 2014-09-14 17:24:16 +03:00
parent feaaf4c5da
commit ffefe46eed
19 changed files with 359 additions and 111 deletions

View file

@ -3,11 +3,8 @@ package com.nutomic.syncthingandroid.test;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources;
import android.test.mock.MockContext;
import junit.framework.Test;
import java.io.File; import java.io.File;
import java.util.ArrayList; 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. * Context that saves all received intents, which can be retrieved later by test classes.
*/ */
public class TestContext extends MockContext { public class MockContext extends ContextWrapper {
private Context mContext;
private ArrayList<Intent> mReceivedIntents = new ArrayList<>(); private ArrayList<Intent> 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 * Use the actual context for calls that aren't easily mocked. May be null if those
* calls aren't needed. * calls aren't needed.
*/ */
public TestContext(Context context) { public MockContext(Context context) {
mContext = context; super(context);
} }
@Override @Override
@ -35,11 +30,6 @@ public class TestContext extends MockContext {
return null; return null;
} }
@Override
public Object getSystemService(String name) {
return mContext.getSystemService(name);
}
@Override @Override
public ComponentName startService(Intent intent) { public ComponentName startService(Intent intent) {
mReceivedIntents.add(intent); mReceivedIntents.add(intent);
@ -67,13 +57,9 @@ public class TestContext extends MockContext {
@Override @Override
public File getFilesDir() { public File getFilesDir() {
File testFilesDir = new File(mContext.getFilesDir(), "test/"); File testFilesDir = new File(super.getFilesDir(), "test/");
testFilesDir.mkdir(); testFilesDir.mkdir();
return testFilesDir; return testFilesDir;
} }
@Override
public Resources getResources() {
return mContext.getResources();
}
} }

View file

@ -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 <T> void setValue(String name, String key, T value, boolean isArray, Activity activity) {
throw new UnsupportedOperationException();
}
@Override
public List<Node> getNodes() {
throw new UnsupportedOperationException();
}
@Override
public void getSystemInfo(final OnReceiveSystemInfoListener listener) {
throw new UnsupportedOperationException();
}
@Override
public List<Repo> 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();
}
}

View file

@ -1,6 +1,5 @@
package com.nutomic.syncthingandroid.test; package com.nutomic.syncthingandroid.test;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
@ -8,7 +7,6 @@ import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
public class MockSyncthingService extends SyncthingService { public class MockSyncthingService extends SyncthingService {
@ -51,7 +49,7 @@ public class MockSyncthingService extends SyncthingService {
@Override @Override
public RestApi getApi() { public RestApi getApi() {
throw new UnsupportedOperationException(); return new MockRestApi(this, null, null);
} }
@Override @Override

View file

@ -7,18 +7,18 @@ import android.test.suitebuilder.annotation.MediumTest;
import com.nutomic.syncthingandroid.syncthing.BatteryReceiver; import com.nutomic.syncthingandroid.syncthing.BatteryReceiver;
import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.test.TestContext; import com.nutomic.syncthingandroid.test.MockContext;
public class BatteryReceiverTest extends AndroidTestCase { public class BatteryReceiverTest extends AndroidTestCase {
private BatteryReceiver mReceiver; private BatteryReceiver mReceiver;
private TestContext mContext; private MockContext mContext;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mReceiver = new BatteryReceiver(); mReceiver = new BatteryReceiver();
mContext = new TestContext(null); mContext = new MockContext(null);
} }
@MediumTest @MediumTest

View file

@ -6,7 +6,7 @@ import android.test.suitebuilder.annotation.MediumTest;
import com.nutomic.syncthingandroid.syncthing.BootReceiver; import com.nutomic.syncthingandroid.syncthing.BootReceiver;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; 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 * 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 { public class BootReceiverTest extends AndroidTestCase {
private BootReceiver mReceiver; private BootReceiver mReceiver;
private TestContext mContext; private MockContext mContext;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mReceiver = new BootReceiver(); mReceiver = new BootReceiver();
mContext = new TestContext(null); mContext = new MockContext(null);
} }
@MediumTest @MediumTest

View file

@ -4,21 +4,20 @@ import android.content.Intent;
import android.os.BatteryManager; import android.os.BatteryManager;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder;
import com.nutomic.syncthingandroid.test.TestContext; import com.nutomic.syncthingandroid.test.MockContext;
public class DeviceStateHolderTest extends AndroidTestCase { public class DeviceStateHolderTest extends AndroidTestCase {
private DeviceStateHolder mReceiver; private DeviceStateHolder mReceiver;
private TestContext mContext; private MockContext mContext;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mReceiver = new DeviceStateHolder(getContext()); mReceiver = new DeviceStateHolder(getContext());
mContext = new TestContext(null); mContext = new MockContext(null);
} }
@MediumTest @MediumTest

View file

@ -3,7 +3,6 @@ package com.nutomic.syncthingandroid.test.syncthing;
import android.net.Uri; import android.net.Uri;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import com.nutomic.syncthingandroid.syncthing.GetTask; import com.nutomic.syncthingandroid.syncthing.GetTask;
import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.RestApi;

View file

@ -4,10 +4,10 @@ import android.content.Intent;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import com.nutomic.syncthingandroid.syncthing.NetworkReceiver;
import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder; import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder;
import com.nutomic.syncthingandroid.syncthing.NetworkReceiver;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; 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 * Tests for correct extras on the Intent sent by
@ -19,13 +19,13 @@ import com.nutomic.syncthingandroid.test.TestContext;
public class NetworkReceiverTest extends AndroidTestCase { public class NetworkReceiverTest extends AndroidTestCase {
private NetworkReceiver mReceiver; private NetworkReceiver mReceiver;
private TestContext mContext; private MockContext mContext;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mReceiver = new NetworkReceiver(); mReceiver = new NetworkReceiver();
mContext = new TestContext(getContext()); mContext = new MockContext(getContext());
} }
@MediumTest @MediumTest

View file

@ -4,10 +4,9 @@ import android.test.AndroidTestCase;
import com.nutomic.syncthingandroid.syncthing.PollWebGuiAvailableTask; import com.nutomic.syncthingandroid.syncthing.PollWebGuiAvailableTask;
import com.nutomic.syncthingandroid.syncthing.PostTask; import com.nutomic.syncthingandroid.syncthing.PostTask;
import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; 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 com.nutomic.syncthingandroid.util.ConfigXml;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -23,7 +22,7 @@ public class PollWebGuiAvailableTaskTest extends AndroidTestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mConfig = new ConfigXml(new TestContext(getContext())); mConfig = new ConfigXml(new MockContext(getContext()));
mConfig.updateIfNeeded(); mConfig.updateIfNeeded();
} }
@ -31,11 +30,11 @@ public class PollWebGuiAvailableTaskTest extends AndroidTestCase {
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
super.tearDown(); super.tearDown();
ConfigXml.getConfigFile(new TestContext(getContext())).delete(); ConfigXml.getConfigFile(new MockContext(getContext())).delete();
} }
public void testPolling() throws InterruptedException { public void testPolling() throws InterruptedException {
mSyncthing = new SyncthingRunnable(new TestContext(null), mSyncthing = new SyncthingRunnable(new MockContext(null),
getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME); getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME);
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);

View file

@ -3,7 +3,6 @@ package com.nutomic.syncthingandroid.test.syncthing;
import android.net.Uri; import android.net.Uri;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import com.nutomic.syncthingandroid.syncthing.GetTask; import com.nutomic.syncthingandroid.syncthing.GetTask;
import com.nutomic.syncthingandroid.syncthing.PostTask; import com.nutomic.syncthingandroid.syncthing.PostTask;

View file

@ -10,7 +10,7 @@ import com.nutomic.syncthingandroid.syncthing.PostTask;
import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable;
import com.nutomic.syncthingandroid.syncthing.SyncthingService; 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 com.nutomic.syncthingandroid.util.ConfigXml;
import java.util.ArrayList; import java.util.ArrayList;
@ -30,10 +30,10 @@ public class RestApiTest extends AndroidTestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mSyncthing = new SyncthingRunnable(new TestContext(null), mSyncthing = new SyncthingRunnable(new MockContext(null),
getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME); getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME);
mConfig = new ConfigXml(new TestContext(getContext())); mConfig = new ConfigXml(new MockContext(getContext()));
mConfig.createCameraRepo(); mConfig.createCameraRepo();
mConfig.updateIfNeeded(); mConfig.updateIfNeeded();
@ -69,7 +69,7 @@ public class RestApiTest extends AndroidTestCase {
} }
}.execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN, mSyncthing.getApiKey()); }.execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN, mSyncthing.getApiKey());
latch.await(1, TimeUnit.SECONDS); latch.await(1, TimeUnit.SECONDS);
ConfigXml.getConfigFile(new TestContext(getContext())).delete(); ConfigXml.getConfigFile(new MockContext(getContext())).delete();
} }
@SmallTest @SmallTest

View file

@ -4,7 +4,7 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable; import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable;
import com.nutomic.syncthingandroid.test.TestContext; import com.nutomic.syncthingandroid.test.MockContext;
import java.io.File; import java.io.File;
@ -12,7 +12,7 @@ public class SyncthingRunnableTest extends AndroidTestCase {
@SmallTest @SmallTest
public void testRunning() throws InterruptedException { public void testRunning() throws InterruptedException {
TestContext context = new TestContext(getContext()); MockContext context = new MockContext(getContext());
File testFile = new File(context.getFilesDir(), "was_running"); File testFile = new File(context.getFilesDir(), "was_running");
assertFalse(testFile.exists()); assertFalse(testFile.exists());
// Inject a differenct command instead of the syncthing binary for testing. // Inject a differenct command instead of the syncthing binary for testing.
@ -23,7 +23,7 @@ public class SyncthingRunnableTest extends AndroidTestCase {
@SmallTest @SmallTest
public void testApiKey() { 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()); assertEquals(20, st.getApiKey().length());
} }

View file

@ -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<SyncthingService> {
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());
}
}

View file

@ -1,21 +1,15 @@
package com.nutomic.syncthingandroid.test.util; package com.nutomic.syncthingandroid.test.util;
import android.content.Intent;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.test.MockContext;
import com.nutomic.syncthingandroid.syncthing.SyncthingRunnable;
import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.test.TestContext;
import com.nutomic.syncthingandroid.util.ConfigXml; import com.nutomic.syncthingandroid.util.ConfigXml;
import java.io.File;
public class ConfigXmlTest extends AndroidTestCase { public class ConfigXmlTest extends AndroidTestCase {
private TestContext mContext; private MockContext mContext;
private ConfigXml mConfig; private ConfigXml mConfig;
@ -23,7 +17,7 @@ public class ConfigXmlTest extends AndroidTestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mContext = new TestContext(getContext()); mContext = new MockContext(getContext());
assertFalse(ConfigXml.getConfigFile(mContext).exists()); assertFalse(ConfigXml.getConfigFile(mContext).exists());
mConfig = new ConfigXml(mContext); mConfig = new ConfigXml(mContext);
assertTrue(ConfigXml.getConfigFile(mContext).exists()); assertTrue(ConfigXml.getConfigFile(mContext).exists());

View file

@ -7,11 +7,8 @@ import android.widget.TextView;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.test.TestContext;
import com.nutomic.syncthingandroid.util.NodesAdapter; import com.nutomic.syncthingandroid.util.NodesAdapter;
import com.nutomic.syncthingandroid.util.ReposAdapter;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;

View file

@ -7,7 +7,6 @@ import android.widget.TextView;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.RestApi;
import com.nutomic.syncthingandroid.test.TestContext;
import com.nutomic.syncthingandroid.util.ReposAdapter; import com.nutomic.syncthingandroid.util.ReposAdapter;
import java.util.ArrayList; import java.util.ArrayList;

View file

@ -146,15 +146,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
private String mLocalNodeId; private String mLocalNodeId;
private final NotificationManager mNotificationManager;
private boolean mRestartPostponed = false; private boolean mRestartPostponed = false;
/** /**
* Stores the result of the last successful request to {@link GetTask#URI_CONNECTIONS}, * Stores the result of the last successful request to {@link GetTask#URI_CONNECTIONS},
* or an empty HashMap. * or an empty HashMap.
*/ */
private HashMap<String, Connection> mPreviousConnections = new HashMap<String, Connection>(); private HashMap<String, Connection> mPreviousConnections = new HashMap<>();
/** /**
* Stores the timestamp of the last successful request to {@link GetTask#URI_CONNECTIONS}. * 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) { public RestApi(Context context, String url, OnApiAvailableListener listener) {
mContext = context; mContext = context;
mUrl = url; mUrl = url;
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mOnApiAvailableListener = listener; mOnApiAvailableListener = listener;
} }
@ -257,7 +253,13 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
* Stops syncthing. You should probably use SyncthingService.stopService() instead. * Stops syncthing. You should probably use SyncthingService.stopService() instead.
*/ */
public void shutdown() { 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); new PostTask().execute(mUrl, PostTask.URI_SHUTDOWN, mApiKey);
} }
@ -371,7 +373,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
.setContentIntent(pi) .setContentIntent(pi)
.build(); .build();
n.flags |= Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL; 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; mRestartPostponed = true;
} }

View file

@ -2,8 +2,6 @@ package com.nutomic.syncthingandroid.syncthing;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -12,25 +10,14 @@ import android.content.SharedPreferences;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.IBinder; import android.os.IBinder;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.MainActivity;
import com.nutomic.syncthingandroid.activities.SettingsActivity; import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.util.ConfigXml; 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.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -42,8 +29,6 @@ public class SyncthingService extends Service {
private static final String TAG = "SyncthingService"; private static final String TAG = "SyncthingService";
private static final int NOTIFICATION_RUNNING = 1;
/** /**
* Intent action to perform a syncthing restart. * 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. * 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. * 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 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 ConfigXml mConfig;
private RestApi mApi; 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 * Checks according to preferences and charging/wifi state, whether syncthing should be enabled
* or not. * or not.
* <p/> *
* Depending on the result, syncthing is started or stopped, and {@link #onApiChange()} is * Depending on the result, syncthing is started or stopped, and {@link #onApiChange()} is
* called. * called.
*/ */
public void updateState() { public void updateState() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean prefStopMobileData = prefs.getBoolean("stop_sync_on_mobile_data", true); boolean prefStopMobileData = prefs.getBoolean(PREF_SYNC_ONLY_WIFI, true);
boolean prefStopNotCharging = prefs.getBoolean("stop_sync_while_not_charging", true); boolean prefStopNotCharging = prefs.getBoolean(PREF_SYNC_ONLY_CHARGING, true);
// Start syncthing. // Start syncthing.
if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) && if ((mDeviceStateHolder.isCharging() || !prefStopNotCharging) &&
@ -187,7 +176,7 @@ public class SyncthingService extends Service {
/** /**
* Move config file, keys, and index files to "official" folder * Move config file, keys, and index files to "official" folder
* <p/> *
* Intended to bring the file locations in older installs in line with * Intended to bring the file locations in older installs in line with
* newer versions. * newer versions.
*/ */
@ -221,18 +210,6 @@ public class SyncthingService extends Service {
*/ */
@Override @Override
public void onCreate() { 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); mDeviceStateHolder = new DeviceStateHolder(SyncthingService.this);
registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); registerReceiver(mDeviceStateHolder, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
new StartupTask().execute(); 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 * 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 * version, and reads syncthing URL..
* {@code Pair<String, String>}. TODO
*/ */
private class StartupTask extends AsyncTask<Void, Void, String> { private class StartupTask extends AsyncTask<Void, Void, String> {
@Override @Override
@ -272,7 +248,7 @@ public class SyncthingService extends Service {
Log.i(TAG, "Web GUI will be available at " + mConfig.getWebGuiUrl()); Log.i(TAG, "Web GUI will be available at " + mConfig.getWebGuiUrl());
// HACK: Make sure there is no syncthing binary left running from an improper // 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. // NOTE: This will log an exception if syncthing is not actually running.
mApi.shutdown(); mApi.shutdown();
updateState(); updateState();
@ -291,12 +267,14 @@ public class SyncthingService extends Service {
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
Log.i(TAG, "Shutting down service"); Log.i(TAG, "Shutting down service");
if (mApi != null) {
mApi.shutdown(); mApi.shutdown();
} }
}
/** /**
* Register a listener for the web gui becoming available.. * Register a listener for the web gui becoming available..
* <p/> *
* If the web gui is already available, listener will be called immediately. * If the web gui is already available, listener will be called immediately.
* Listeners are unregistered automatically after being called. * 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). * Returns true if this service has not been started before (ie config.xml does not exist).
* <p/> *
* This will return true until the public key file has been generated. * This will return true until the public key file has been generated.
*/ */
public boolean isFirstStart() { public boolean isFirstStart() {
@ -323,7 +301,7 @@ public class SyncthingService extends Service {
/** /**
* Register a listener for the syncthing API state changing. * Register a listener for the syncthing API state changing.
* <p/> *
* The listener is called immediately with the current state, and again whenever the state * The listener is called immediately with the current state, and again whenever the state
* changes. * 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 // Make sure we don't send an invalid state or syncthing might shwow a "disabled" message
// when it's just starting up. // when it's just starting up.
listener.onApiChange(mCurrentState); listener.onApiChange(mCurrentState);
mOnApiChangeListeners.add(new WeakReference<OnApiChangeListener>(listener)); mOnApiChangeListeners.add(new WeakReference<>(listener));
} }
private class PollWebGuiAvailableTaskImpl extends PollWebGuiAvailableTask { private class PollWebGuiAvailableTaskImpl extends PollWebGuiAvailableTask {

View file

@ -15,7 +15,6 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Random;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;