Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b84a25c authored by Stefan Niedermann's avatar Stefan Niedermann
Browse files

Harden ApiProvider for concurrent modifications and add unit tests

parent 6b3bcdfb
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -16,8 +16,8 @@ import com.nextcloud.android.sso.api.NextcloudAPI;
import com.nextcloud.android.sso.model.SingleSignOnAccount;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import it.niedermann.owncloud.notes.persistence.sync.CapabilitiesDeserializer;
import it.niedermann.owncloud.notes.persistence.sync.NotesAPI;
@@ -40,10 +40,10 @@ public class ApiProvider {

    private static final String API_ENDPOINT_OCS = "/ocs/v2.php/cloud/";

    private static final Map<String, NextcloudAPI> API_CACHE = new HashMap<>();
    private static final Map<String, NextcloudAPI> API_CACHE = new ConcurrentHashMap<>();

    private static final Map<String, OcsAPI> API_CACHE_OCS = new HashMap<>();
    private static final Map<String, NotesAPI> API_CACHE_NOTES = new HashMap<>();
    private static final Map<String, OcsAPI> API_CACHE_OCS = new ConcurrentHashMap<>();
    private static final Map<String, NotesAPI> API_CACHE_NOTES = new ConcurrentHashMap<>();

    public static ApiProvider getInstance() {
        return INSTANCE;
+96 −0
Original line number Diff line number Diff line
package it.niedermann.owncloud.notes.persistence;

import android.os.Build;

import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
import androidx.test.core.app.ApplicationProvider;

import com.nextcloud.android.sso.model.SingleSignOnAccount;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import it.niedermann.owncloud.notes.persistence.sync.NotesAPI;
import it.niedermann.owncloud.notes.persistence.sync.OcsAPI;
import it.niedermann.owncloud.notes.shared.model.ApiVersion;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;

@RunWith(RobolectricTestRunner.class)
@Config(sdk = {Build.VERSION_CODES.P})
public class ApiProviderTest {

    @Rule
    public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();

    private ApiProvider apiProvider;
    private SingleSignOnAccount ssoAccount;
    private SingleSignOnAccount secondSsoAccount;

    @Before
    public void setup() {
        apiProvider = ApiProvider.getInstance();
        ssoAccount = new SingleSignOnAccount("one", "eins", "1", "example.com", "");
        secondSsoAccount = new SingleSignOnAccount("two", "zwei", "2", "example.org", "");
    }

    @Test
    public void testGetOcsAPI() {
        OcsAPI api = apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), ssoAccount);

        assertNotNull(api);
        assertSame(api, apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), ssoAccount));
        assertNotSame(api, apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), secondSsoAccount));

        apiProvider.invalidateAPICache(ssoAccount);

        final OcsAPI newApi = apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), ssoAccount);

        assertNotSame("After invalidating the cache, a new API instance is returned", api, newApi);

        api = newApi;

        assertSame(api, apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), ssoAccount));
        assertNotSame("A new instance should be returned when another SingleSignOn account is provided",
                api, apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), secondSsoAccount));

        apiProvider.invalidateAPICache();

        assertNotSame(api, apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), ssoAccount));
        assertNotSame(newApi, apiProvider.getOcsAPI(ApplicationProvider.getApplicationContext(), secondSsoAccount));
    }

    @Test
    public void testGetNotesAPI() {
        final NotesAPI notesAPI = apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), ssoAccount, ApiVersion.API_VERSION_0_2);

        assertNotNull(notesAPI);

        assertSame("Before a manual invalidation, the returned Notes API will be the same instance",
                notesAPI, apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), ssoAccount, ApiVersion.API_VERSION_0_2));
        assertSame("Before a manual invalidation, the returned Notes API will be the same instance, no matter which preferred version is passed",
                notesAPI, apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), ssoAccount, ApiVersion.API_VERSION_1_0));

        apiProvider.invalidateAPICache();

        final NotesAPI newNotesAPI = apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), ssoAccount, ApiVersion.API_VERSION_1_0);

        assertNotSame("After a manual invalidation, the returned Notes API will be a new instance",
                notesAPI, newNotesAPI);
        assertSame("Before a manual invalidation, the returned Notes API will be the same instance, no matter which preferred version is passed",
                newNotesAPI, apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), ssoAccount, ApiVersion.API_VERSION_0_2));
        assertNotSame("Before a manual invalidation, the returned Notes API will be a different instance, even if the preferred version is the same, if the ssoAccount is different",
                newNotesAPI, apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), secondSsoAccount, ApiVersion.API_VERSION_0_2));

        apiProvider.invalidateAPICache(ssoAccount);

        assertNotSame("After a manual invalidation, the returned Notes API will be a new instance",
                newNotesAPI, apiProvider.getNotesAPI(ApplicationProvider.getApplicationContext(), ssoAccount, ApiVersion.API_VERSION_0_2));
    }
}
 No newline at end of file