repo created
This commit is contained in:
commit
1ef725ef20
2483 changed files with 278273 additions and 0 deletions
544
app/src/androidTest/java/com/owncloud/android/AbstractIT.java
Normal file
544
app/src/androidTest/java/com/owncloud/android/AbstractIT.java
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2018 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.testing.screenshot.Screenshot;
|
||||
import com.facebook.testing.screenshot.internal.TestNameDetector;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.nextcloud.client.device.BatteryStatus;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.jobs.upload.FileUploadWorker;
|
||||
import com.nextcloud.client.network.Connectivity;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.nextcloud.client.preferences.AppPreferencesImpl;
|
||||
import com.nextcloud.client.preferences.DarkMode;
|
||||
import com.nextcloud.common.NextcloudClient;
|
||||
import com.nextcloud.test.GrantStoragePermissionRule;
|
||||
import com.nextcloud.test.RandomStringGenerator;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientFactory;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
|
||||
import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.status.OCCapability;
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
||||
import com.owncloud.android.operations.CreateFolderOperation;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.test.espresso.contrib.DrawerActions;
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
|
||||
import androidx.test.runner.lifecycle.Stage;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
/**
|
||||
* Common base for all integration tests.
|
||||
*/
|
||||
public abstract class AbstractIT {
|
||||
@Rule
|
||||
public final TestRule permissionRule = GrantStoragePermissionRule.grant();
|
||||
|
||||
protected static OwnCloudClient client;
|
||||
protected static NextcloudClient nextcloudClient;
|
||||
protected static Account account;
|
||||
protected static User user;
|
||||
protected static Context targetContext;
|
||||
protected static String DARK_MODE = "";
|
||||
protected static String COLOR = "";
|
||||
|
||||
protected Activity currentActivity;
|
||||
|
||||
protected FileDataStorageManager fileDataStorageManager =
|
||||
new FileDataStorageManager(user, targetContext.getContentResolver());
|
||||
|
||||
protected ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(targetContext);
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAll() {
|
||||
try {
|
||||
// clean up
|
||||
targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
AccountManager platformAccountManager = AccountManager.get(targetContext);
|
||||
|
||||
for (Account account : platformAccountManager.getAccounts()) {
|
||||
if (account.type.equalsIgnoreCase("nextcloud")) {
|
||||
platformAccountManager.removeAccountExplicitly(account);
|
||||
}
|
||||
}
|
||||
|
||||
account = createAccount("test@https://nextcloud.localhost");
|
||||
user = getUser(account);
|
||||
|
||||
client = OwnCloudClientFactory.createOwnCloudClient(account, targetContext);
|
||||
nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, targetContext);
|
||||
} catch (OperationCanceledException |
|
||||
IOException |
|
||||
AccountUtils.AccountNotFoundException |
|
||||
AuthenticatorException e) {
|
||||
throw new RuntimeException("Error setting up clients", e);
|
||||
}
|
||||
|
||||
Bundle arguments = androidx.test.platform.app.InstrumentationRegistry.getArguments();
|
||||
|
||||
// color
|
||||
String colorParameter = arguments.getString("COLOR");
|
||||
if (!TextUtils.isEmpty(colorParameter)) {
|
||||
FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(user,
|
||||
targetContext.getContentResolver());
|
||||
|
||||
String colorHex = null;
|
||||
COLOR = colorParameter;
|
||||
switch (colorParameter) {
|
||||
case "red":
|
||||
colorHex = "#7c0000";
|
||||
break;
|
||||
|
||||
case "green":
|
||||
colorHex = "#00ff00";
|
||||
break;
|
||||
|
||||
case "white":
|
||||
colorHex = "#ffffff";
|
||||
break;
|
||||
|
||||
case "black":
|
||||
colorHex = "#000000";
|
||||
break;
|
||||
|
||||
case "lightgreen":
|
||||
colorHex = "#aaff00";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
OCCapability capability = fileDataStorageManager.getCapability(account.name);
|
||||
capability.setGroupfolders(CapabilityBooleanType.TRUE);
|
||||
|
||||
if (colorHex != null) {
|
||||
capability.setServerColor(colorHex);
|
||||
}
|
||||
|
||||
fileDataStorageManager.saveCapabilities(capability);
|
||||
}
|
||||
|
||||
// dark / light
|
||||
String darkModeParameter = arguments.getString("DARKMODE");
|
||||
|
||||
if (darkModeParameter != null) {
|
||||
if (darkModeParameter.equalsIgnoreCase("dark")) {
|
||||
DARK_MODE = "dark";
|
||||
AppPreferencesImpl.fromContext(targetContext).setDarkThemeMode(DarkMode.DARK);
|
||||
MainApp.setAppTheme(DarkMode.DARK);
|
||||
} else {
|
||||
DARK_MODE = "light";
|
||||
}
|
||||
}
|
||||
|
||||
if (DARK_MODE.equalsIgnoreCase("light") && COLOR.equalsIgnoreCase("blue")) {
|
||||
// use already existing names
|
||||
DARK_MODE = "";
|
||||
COLOR = "";
|
||||
}
|
||||
}
|
||||
|
||||
protected void testOnlyOnServer(OwnCloudVersion version) throws AccountUtils.AccountNotFoundException {
|
||||
OCCapability ocCapability = getCapability();
|
||||
assumeTrue(ocCapability.getVersion().isNewerOrEqual(version));
|
||||
}
|
||||
|
||||
protected OCCapability getCapability() throws AccountUtils.AccountNotFoundException {
|
||||
NextcloudClient client = OwnCloudClientFactory.createNextcloudClient(user, targetContext);
|
||||
|
||||
OCCapability ocCapability = (OCCapability) new GetCapabilitiesRemoteOperation()
|
||||
.execute(client)
|
||||
.getSingleData();
|
||||
|
||||
return ocCapability;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void enableAccessibilityChecks() {
|
||||
androidx.test.espresso.accessibility.AccessibilityChecks.enable().setRunChecksFromRootView(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
fileDataStorageManager.removeLocalFiles(user, fileDataStorageManager);
|
||||
fileDataStorageManager.deleteAllFiles();
|
||||
}
|
||||
|
||||
protected FileDataStorageManager getStorageManager() {
|
||||
return fileDataStorageManager;
|
||||
}
|
||||
|
||||
protected Account[] getAllAccounts() {
|
||||
return AccountManager.get(targetContext).getAccounts();
|
||||
}
|
||||
|
||||
protected static void createDummyFiles() throws IOException {
|
||||
File tempPath = new File(FileStorageUtils.getTemporalPath(account.name));
|
||||
if (!tempPath.exists()) {
|
||||
assertTrue(tempPath.mkdirs());
|
||||
}
|
||||
|
||||
assertTrue(tempPath.exists());
|
||||
|
||||
createFile("empty.txt", 0);
|
||||
createFile("nonEmpty.txt", 100);
|
||||
createFile("chunkedFile.txt", 500000);
|
||||
}
|
||||
|
||||
protected static File getDummyFile(String name) throws IOException {
|
||||
File file = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + File.separator + name);
|
||||
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
} else if (name.endsWith("/")) {
|
||||
file.mkdirs();
|
||||
return file;
|
||||
} else {
|
||||
switch (name) {
|
||||
case "empty.txt":
|
||||
return createFile("empty.txt", 0);
|
||||
|
||||
case "nonEmpty.txt":
|
||||
return createFile("nonEmpty.txt", 100);
|
||||
|
||||
case "chunkedFile.txt":
|
||||
return createFile("chunkedFile.txt", 500000);
|
||||
|
||||
default:
|
||||
return createFile(name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static File createFile(String name, int iteration) throws IOException {
|
||||
File file = new File(FileStorageUtils.getTemporalPath(account.name) + File.separator + name);
|
||||
if (!file.getParentFile().exists()) {
|
||||
assertTrue(file.getParentFile().mkdirs());
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
|
||||
FileWriter writer = new FileWriter(file);
|
||||
|
||||
for (int i = 0; i < iteration; i++) {
|
||||
writer.write("123123123123123123123123123\n");
|
||||
}
|
||||
writer.flush();
|
||||
writer.close();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
protected File getFile(String filename) throws IOException {
|
||||
InputStream inputStream = getInstrumentation().getContext().getAssets().open(filename);
|
||||
File temp = File.createTempFile("file", "file");
|
||||
FileUtils.copyInputStreamToFile(inputStream, temp);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
protected void waitForIdleSync() {
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
protected void onIdleSync(Runnable recipient) {
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdle(recipient);
|
||||
}
|
||||
|
||||
protected void openDrawer(IntentsTestRule activityRule) {
|
||||
Activity sut = activityRule.launchActivity(null);
|
||||
|
||||
shortSleep();
|
||||
|
||||
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
|
||||
|
||||
waitForIdleSync();
|
||||
|
||||
screenshot(sut);
|
||||
}
|
||||
|
||||
protected Activity getCurrentActivity() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
Collection<Activity> resumedActivities = ActivityLifecycleMonitorRegistry
|
||||
.getInstance()
|
||||
.getActivitiesInStage(Stage.RESUMED);
|
||||
|
||||
if (resumedActivities.iterator().hasNext()) {
|
||||
currentActivity = resumedActivities.iterator().next();
|
||||
}
|
||||
});
|
||||
|
||||
return currentActivity;
|
||||
}
|
||||
|
||||
protected static void shortSleep() {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void longSleep() {
|
||||
try {
|
||||
Thread.sleep(20000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void sleep(int second) {
|
||||
try {
|
||||
Thread.sleep(1000L * second);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public OCFile createFolder(String remotePath) {
|
||||
RemoteOperationResult check = new ExistenceCheckRemoteOperation(remotePath, false).execute(client);
|
||||
|
||||
if (!check.isSuccess()) {
|
||||
assertTrue(new CreateFolderOperation(remotePath, user, targetContext, getStorageManager())
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
}
|
||||
|
||||
return getStorageManager().getFileByDecryptedRemotePath(remotePath.endsWith("/") ? remotePath : remotePath + "/");
|
||||
}
|
||||
|
||||
public void uploadFile(File file, String remotePath) {
|
||||
OCUpload ocUpload = new OCUpload(file.getAbsolutePath(), remotePath, account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
}
|
||||
|
||||
public void uploadOCUpload(OCUpload ocUpload) {
|
||||
ConnectivityService connectivityServiceMock = new ConnectivityService() {
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternetWalled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connectivity getConnectivity() {
|
||||
return Connectivity.CONNECTED_WIFI;
|
||||
}
|
||||
};
|
||||
|
||||
PowerManagementService powerManagementServiceMock = new PowerManagementService() {
|
||||
@NonNull
|
||||
@Override
|
||||
public BatteryStatus getBattery() {
|
||||
return new BatteryStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingExclusionAvailable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
UserAccountManager accountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(accountManager,
|
||||
targetContext.getContentResolver());
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertTrue(result.getLogMessage(), result.isSuccess());
|
||||
}
|
||||
|
||||
protected void enableRTL() {
|
||||
Locale locale = new Locale("ar");
|
||||
Resources resources = InstrumentationRegistry.getInstrumentation().getTargetContext().getResources();
|
||||
Configuration config = resources.getConfiguration();
|
||||
config.setLocale(locale);
|
||||
resources.updateConfiguration(config, null);
|
||||
}
|
||||
|
||||
protected void resetLocale() {
|
||||
Locale locale = new Locale("en");
|
||||
Resources resources = InstrumentationRegistry.getInstrumentation().getTargetContext().getResources();
|
||||
Configuration config = resources.getConfiguration();
|
||||
config.setLocale(locale);
|
||||
resources.updateConfiguration(config, null);
|
||||
}
|
||||
|
||||
protected void screenshot(View view) {
|
||||
screenshot(view, "");
|
||||
}
|
||||
|
||||
protected void screenshotViaName(Activity activity, String name) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
Screenshot.snapActivity(activity).setName(name).record();
|
||||
}
|
||||
}
|
||||
|
||||
protected void screenshot(View view, String prefix) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
Screenshot.snap(view).setName(createName(prefix)).record();
|
||||
}
|
||||
}
|
||||
|
||||
protected void screenshot(Activity sut) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
Screenshot.snapActivity(sut).setName(createName()).record();
|
||||
}
|
||||
}
|
||||
|
||||
protected void screenshot(DialogFragment dialogFragment, String prefix) {
|
||||
screenshot(Objects.requireNonNull(dialogFragment.requireDialog().getWindow()).getDecorView(), prefix);
|
||||
}
|
||||
|
||||
private String createName() {
|
||||
return createName("");
|
||||
}
|
||||
|
||||
public String createName(String name, String prefix) {
|
||||
if (!TextUtils.isEmpty(prefix)) {
|
||||
name = name + "_" + prefix;
|
||||
}
|
||||
|
||||
if (!DARK_MODE.isEmpty()) {
|
||||
name = name + "_" + DARK_MODE;
|
||||
}
|
||||
|
||||
if (!COLOR.isEmpty()) {
|
||||
name = name + "_" + COLOR;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private String createName(String prefix) {
|
||||
String name = TestNameDetector.getTestClass() + "_" + TestNameDetector.getTestName();
|
||||
return createName(name, prefix);
|
||||
}
|
||||
|
||||
public static String getUserId(User user) {
|
||||
return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID);
|
||||
}
|
||||
|
||||
public String getRandomName() {
|
||||
return getRandomName(5);
|
||||
}
|
||||
|
||||
public String getRandomName(int length) {
|
||||
return RandomStringGenerator.make(length);
|
||||
}
|
||||
|
||||
protected static User getUser(Account account) {
|
||||
Optional<User> optionalUser = UserAccountManagerImpl.fromContext(targetContext).getUser(account.name);
|
||||
return optionalUser.orElseThrow(IllegalAccessError::new);
|
||||
}
|
||||
|
||||
protected static Account createAccount(String name) {
|
||||
AccountManager platformAccountManager = AccountManager.get(targetContext);
|
||||
|
||||
Account temp = new Account(name, MainApp.getAccountType(targetContext));
|
||||
int atPos = name.lastIndexOf('@');
|
||||
platformAccountManager.addAccountExplicitly(temp, "password", null);
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL,
|
||||
name.substring(atPos + 1));
|
||||
platformAccountManager.setUserData(temp, KEY_USER_ID, name.substring(0, atPos));
|
||||
|
||||
Account account = UserAccountManagerImpl.fromContext(targetContext).getAccountByName(name);
|
||||
if (account == null) {
|
||||
throw new ActivityNotFoundException();
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
protected static boolean removeAccount(Account account) {
|
||||
return AccountManager.get(targetContext).removeAccountExplicitly(account);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.nextcloud.client.device.BatteryStatus;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.jobs.upload.FileUploadWorker;
|
||||
import com.nextcloud.client.network.Connectivity;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientFactory;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.resources.e2ee.ToggleEncryptionRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile;
|
||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Common base for all integration tests.
|
||||
*/
|
||||
public abstract class AbstractOnServerIT extends AbstractIT {
|
||||
@BeforeClass
|
||||
public static void beforeAll() {
|
||||
try {
|
||||
// clean up
|
||||
targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
AccountManager platformAccountManager = AccountManager.get(targetContext);
|
||||
|
||||
for (Account account : platformAccountManager.getAccounts()) {
|
||||
if (account.type.equalsIgnoreCase("nextcloud")) {
|
||||
platformAccountManager.removeAccountExplicitly(account);
|
||||
}
|
||||
}
|
||||
|
||||
Bundle arguments = androidx.test.platform.app.InstrumentationRegistry.getArguments();
|
||||
|
||||
Uri baseUrl = Uri.parse(arguments.getString("TEST_SERVER_URL"));
|
||||
String loginName = arguments.getString("TEST_SERVER_USERNAME");
|
||||
String password = arguments.getString("TEST_SERVER_PASSWORD");
|
||||
|
||||
Account temp = new Account(loginName + "@" + baseUrl, MainApp.getAccountType(targetContext));
|
||||
UserAccountManager accountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
if (!accountManager.exists(temp)) {
|
||||
platformAccountManager.addAccountExplicitly(temp, password, null);
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION,
|
||||
Integer.toString(UserAccountManager.ACCOUNT_VERSION));
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0");
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString());
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName); // same as userId
|
||||
}
|
||||
|
||||
final UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
account = userAccountManager.getAccountByName(loginName + "@" + baseUrl);
|
||||
|
||||
if (account == null) {
|
||||
throw new ActivityNotFoundException();
|
||||
}
|
||||
|
||||
Optional<User> optionalUser = userAccountManager.getUser(account.name);
|
||||
user = optionalUser.orElseThrow(IllegalAccessError::new);
|
||||
|
||||
client = OwnCloudClientFactory.createOwnCloudClient(account, targetContext);
|
||||
nextcloudClient = OwnCloudClientFactory.createNextcloudClient(user, targetContext);
|
||||
|
||||
createDummyFiles();
|
||||
|
||||
waitForServer(client, baseUrl);
|
||||
|
||||
// deleteAllFilesOnServer(); // makes sure that no file/folder is in root
|
||||
|
||||
} catch (OperationCanceledException |
|
||||
IOException |
|
||||
AccountUtils.AccountNotFoundException |
|
||||
AuthenticatorException e) {
|
||||
throw new RuntimeException("Error setting up clients", e);
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
deleteAllFilesOnServer();
|
||||
|
||||
super.after();
|
||||
}
|
||||
|
||||
public static void deleteAllFilesOnServer() {
|
||||
RemoteOperationResult result = new ReadFolderRemoteOperation("/").execute(client);
|
||||
assertTrue(result.getLogMessage(), result.isSuccess());
|
||||
|
||||
for (Object object : result.getData()) {
|
||||
RemoteFile remoteFile = (RemoteFile) object;
|
||||
|
||||
if (!remoteFile.getRemotePath().equals("/")) {
|
||||
if (remoteFile.isEncrypted()) {
|
||||
ToggleEncryptionRemoteOperation operation = new ToggleEncryptionRemoteOperation(remoteFile.getLocalId(),
|
||||
remoteFile.getRemotePath(),
|
||||
false);
|
||||
|
||||
boolean operationResult = operation
|
||||
.execute(client)
|
||||
.isSuccess();
|
||||
|
||||
assertTrue(operationResult);
|
||||
}
|
||||
|
||||
boolean removeResult = false;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
removeResult = new RemoveFileRemoteOperation(remoteFile.getRemotePath())
|
||||
.execute(client)
|
||||
.isSuccess();
|
||||
|
||||
if (removeResult) {
|
||||
break;
|
||||
}
|
||||
|
||||
shortSleep();
|
||||
}
|
||||
|
||||
assertTrue(removeResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitForServer(OwnCloudClient client, Uri baseUrl) {
|
||||
GetMethod get = new GetMethod(baseUrl + "/status.php");
|
||||
|
||||
try {
|
||||
int i = 0;
|
||||
while (client.executeMethod(get) != HttpStatus.SC_OK && i < 3) {
|
||||
System.out.println("wait…");
|
||||
Thread.sleep(60 * 1000);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 3) {
|
||||
Assert.fail("Server not ready!");
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void uploadOCUpload(OCUpload ocUpload) {
|
||||
uploadOCUpload(ocUpload, FileUploadWorker.LOCAL_BEHAVIOUR_COPY);
|
||||
}
|
||||
|
||||
public void uploadOCUpload(OCUpload ocUpload, int localBehaviour) {
|
||||
ConnectivityService connectivityServiceMock = new ConnectivityService() {
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternetWalled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connectivity getConnectivity() {
|
||||
return Connectivity.CONNECTED_WIFI;
|
||||
}
|
||||
};
|
||||
|
||||
PowerManagementService powerManagementServiceMock = new PowerManagementService() {
|
||||
@NonNull
|
||||
@Override
|
||||
public BatteryStatus getBattery() {
|
||||
return new BatteryStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingExclusionAvailable() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
UserAccountManager accountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(accountManager,
|
||||
targetContext.getContentResolver());
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
localBehaviour,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertTrue(result.getLogMessage(), result.isSuccess());
|
||||
|
||||
OCFile parentFolder = getStorageManager()
|
||||
.getFileByEncryptedRemotePath(new File(ocUpload.getRemotePath()).getParent() + "/");
|
||||
String uploadedFileName = new File(ocUpload.getRemotePath()).getName();
|
||||
OCFile uploadedFile = getStorageManager().
|
||||
getFileByDecryptedRemotePath(parentFolder.getDecryptedRemotePath() + uploadedFileName);
|
||||
|
||||
assertNotNull(uploadedFile.getRemoteId());
|
||||
assertNotNull(uploadedFile.getPermissions());
|
||||
|
||||
if (localBehaviour == FileUploadWorker.LOCAL_BEHAVIOUR_COPY ||
|
||||
localBehaviour == FileUploadWorker.LOCAL_BEHAVIOUR_MOVE) {
|
||||
assertTrue(new File(uploadedFile.getStoragePath()).exists());
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshFolder(String path) {
|
||||
assertTrue(new RefreshFolderOperation(getStorageManager().getFileByEncryptedRemotePath(path),
|
||||
System.currentTimeMillis(),
|
||||
false,
|
||||
false,
|
||||
getStorageManager(),
|
||||
user,
|
||||
targetContext
|
||||
).execute(client).isSuccess());
|
||||
}
|
||||
}
|
||||
108
app/src/androidTest/java/com/owncloud/android/DownloadIT.java
Normal file
108
app/src/androidTest/java/com/owncloud/android/DownloadIT.java
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.DownloadFileOperation;
|
||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests related to file uploads.
|
||||
*/
|
||||
public class DownloadIT extends AbstractOnServerIT {
|
||||
private static final String FOLDER = "/testUpload/";
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
RemoteOperationResult result = new RefreshFolderOperation(getStorageManager().getFileByPath("/"),
|
||||
System.currentTimeMillis() / 1000L,
|
||||
false,
|
||||
true,
|
||||
getStorageManager(),
|
||||
user,
|
||||
targetContext)
|
||||
.execute(client);
|
||||
|
||||
// cleanup only if folder exists
|
||||
if (result.isSuccess() && getStorageManager().getFileByDecryptedRemotePath(FOLDER) != null) {
|
||||
new RemoveFileOperation(getStorageManager().getFileByDecryptedRemotePath(FOLDER),
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyDownload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
|
||||
OCUpload ocUpload2 = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty2.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload2);
|
||||
|
||||
refreshFolder("/");
|
||||
refreshFolder(FOLDER);
|
||||
|
||||
OCFile file1 = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
|
||||
OCFile file2 = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty2.txt");
|
||||
verifyDownload(file1, file2);
|
||||
|
||||
assertTrue(new DownloadFileOperation(user, file1, targetContext).execute(client).isSuccess());
|
||||
assertTrue(new DownloadFileOperation(user, file2, targetContext).execute(client).isSuccess());
|
||||
|
||||
refreshFolder(FOLDER);
|
||||
|
||||
file1 = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
|
||||
file2 = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty2.txt");
|
||||
|
||||
verifyDownload(file1, file2);
|
||||
}
|
||||
|
||||
private void verifyDownload(OCFile file1, OCFile file2) {
|
||||
assertNotNull(file1);
|
||||
assertNotNull(file2);
|
||||
assertNotSame(file1.getStoragePath(), file2.getStoragePath());
|
||||
|
||||
assertTrue(new File(file1.getStoragePath()).exists());
|
||||
assertTrue(new File(file2.getStoragePath()).exists());
|
||||
|
||||
// test against hardcoded path to make sure that it is correct
|
||||
assertEquals("/storage/emulated/0/Android/media/com.nextcloud.client/nextcloud/" +
|
||||
Uri.encode(account.name, "@") + "/testUpload/nonEmpty.txt",
|
||||
file1.getStoragePath());
|
||||
assertEquals("/storage/emulated/0/Android/media/com.nextcloud.client/nextcloud/" +
|
||||
Uri.encode(account.name, "@") + "/testUpload/nonEmpty2.txt",
|
||||
file2.getStoragePath());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.owncloud.android
|
||||
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import java.security.SecureRandom
|
||||
|
||||
open class EncryptionIT : AbstractIT() {
|
||||
|
||||
fun testFolder(): OCFile {
|
||||
val rootPath = "/"
|
||||
val folderPath = "/TestFolder/"
|
||||
|
||||
OCFile(rootPath).apply {
|
||||
storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
return OCFile(folderPath).apply {
|
||||
decryptedRemotePath = folderPath
|
||||
isEncrypted = true
|
||||
fileLength = SecureRandom().nextLong()
|
||||
setFolder()
|
||||
parentId = storageManager.getFileByDecryptedRemotePath(rootPath)!!.fileId
|
||||
storageManager.saveFile(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
150
app/src/androidTest/java/com/owncloud/android/FileIT.java
Normal file
150
app/src/androidTest/java/com/owncloud/android/FileIT.java
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2018 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.CreateFolderOperation;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.operations.RenameFileOperation;
|
||||
import com.owncloud.android.operations.SynchronizeFolderOperation;
|
||||
import com.owncloud.android.operations.common.SyncOperation;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Tests related to file operations.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FileIT extends AbstractOnServerIT {
|
||||
|
||||
@Test
|
||||
public void testCreateFolder() {
|
||||
String path = "/testFolder/";
|
||||
|
||||
// folder does not exist yet
|
||||
assertNull(getStorageManager().getFileByPath(path));
|
||||
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, user, targetContext, getStorageManager());
|
||||
RemoteOperationResult result = syncOp.execute(client);
|
||||
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
|
||||
// folder exists
|
||||
OCFile file = getStorageManager().getFileByPath(path);
|
||||
assertTrue(file.isFolder());
|
||||
|
||||
// cleanup
|
||||
assertTrue(new RemoveFileOperation(file, false, user, false, targetContext, getStorageManager())
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNonExistingSubFolder() {
|
||||
String path = "/subFolder/1/2/3/4/5/";
|
||||
// folder does not exist yet
|
||||
assertNull(getStorageManager().getFileByPath(path));
|
||||
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, user, targetContext, getStorageManager());
|
||||
RemoteOperationResult result = syncOp.execute(client);
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
|
||||
// folder exists
|
||||
OCFile file = getStorageManager().getFileByPath(path);
|
||||
assertTrue(file.isFolder());
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation(file,
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteIdNull() {
|
||||
getStorageManager().deleteAllFiles();
|
||||
assertEquals(0, getStorageManager().getAllFiles().size());
|
||||
|
||||
OCFile test = new OCFile("/123.txt");
|
||||
getStorageManager().saveFile(test);
|
||||
assertEquals(1, getStorageManager().getAllFiles().size());
|
||||
|
||||
getStorageManager().deleteAllFiles();
|
||||
assertEquals(0, getStorageManager().getAllFiles().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameFolder() throws IOException {
|
||||
String folderPath = "/testRenameFolder/";
|
||||
|
||||
// create folder
|
||||
createFolder(folderPath);
|
||||
|
||||
// upload file inside it
|
||||
uploadFile(getDummyFile("nonEmpty.txt"), folderPath + "text.txt");
|
||||
|
||||
// sync folder
|
||||
assertTrue(new SynchronizeFolderOperation(targetContext,
|
||||
folderPath,
|
||||
user,
|
||||
System.currentTimeMillis(),
|
||||
fileDataStorageManager)
|
||||
.execute(targetContext)
|
||||
.isSuccess());
|
||||
|
||||
// check if file exists
|
||||
String storagePath1 = fileDataStorageManager.getFileByDecryptedRemotePath(folderPath).getStoragePath();
|
||||
assertTrue(new File(storagePath1).exists());
|
||||
|
||||
String storagePath2 = fileDataStorageManager
|
||||
.getFileByDecryptedRemotePath(folderPath + "text.txt")
|
||||
.getStoragePath();
|
||||
assertTrue(new File(storagePath2).exists());
|
||||
|
||||
shortSleep();
|
||||
|
||||
// Rename
|
||||
assertTrue(
|
||||
new RenameFileOperation(folderPath, "test123", fileDataStorageManager)
|
||||
.execute(targetContext)
|
||||
.isSuccess()
|
||||
);
|
||||
|
||||
// after rename check new location
|
||||
assertTrue(
|
||||
new File(fileDataStorageManager.getFileByDecryptedRemotePath("/test123/").getStoragePath())
|
||||
.exists()
|
||||
);
|
||||
assertTrue(
|
||||
new File(fileDataStorageManager.getFileByDecryptedRemotePath("/test123/text.txt").getStoragePath())
|
||||
.exists()
|
||||
);
|
||||
|
||||
// old files do no exist
|
||||
assertNull(fileDataStorageManager.getFileByDecryptedRemotePath(folderPath));
|
||||
assertNull(fileDataStorageManager.getFileByDecryptedRemotePath(folderPath + "text.txt"));
|
||||
|
||||
// local files also do not exist
|
||||
assertFalse(new File(storagePath1).exists());
|
||||
assertFalse(new File(storagePath2).exists());
|
||||
}
|
||||
}
|
||||
137
app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java
Normal file
137
app/src/androidTest/java/com/owncloud/android/ScreenshotsIT.java
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2018 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.CreateFolderOperation;
|
||||
import com.owncloud.android.operations.common.SyncOperation;
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||
import com.owncloud.android.ui.activity.SettingsActivity;
|
||||
import com.owncloud.android.ui.activity.SyncedFoldersActivity;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.espresso.action.ViewActions;
|
||||
import androidx.test.espresso.contrib.DrawerActions;
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions;
|
||||
import androidx.test.espresso.matcher.PreferenceMatchers;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import tools.fastlane.screengrab.Screengrab;
|
||||
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;
|
||||
import tools.fastlane.screengrab.locale.LocaleTestRule;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onData;
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.Espresso.pressBack;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.core.AnyOf.anyOf;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(JUnit4.class)
|
||||
public class ScreenshotsIT extends AbstractOnServerIT {
|
||||
@ClassRule
|
||||
public static final LocaleTestRule localeTestRule = new LocaleTestRule();
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeScreenshot() {
|
||||
Screengrab.setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gridViewScreenshot() {
|
||||
ActivityScenario.launch(FileDisplayActivity.class);
|
||||
|
||||
onView(anyOf(withText(R.string.action_switch_grid_view), withId(R.id.switch_grid_view_button))).perform(click());
|
||||
|
||||
shortSleep();
|
||||
|
||||
Screengrab.screenshot("01_gridView");
|
||||
|
||||
onView(anyOf(withText(R.string.action_switch_list_view), withId(R.id.switch_grid_view_button))).perform(click());
|
||||
|
||||
Assert.assertTrue(true); // if we reach this, everything is ok
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listViewScreenshot() {
|
||||
String path = "/Camera/";
|
||||
|
||||
// folder does not exist yet
|
||||
if (getStorageManager().getFileByEncryptedRemotePath(path) == null) {
|
||||
SyncOperation syncOp = new CreateFolderOperation(path, user, targetContext, getStorageManager());
|
||||
RemoteOperationResult result = syncOp.execute(client);
|
||||
|
||||
assertTrue(result.isSuccess());
|
||||
}
|
||||
|
||||
ActivityScenario.launch(FileDisplayActivity.class);
|
||||
|
||||
// go into work folder
|
||||
onView(withId(R.id.list_root)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
|
||||
|
||||
Screengrab.screenshot("02_listView");
|
||||
|
||||
Assert.assertTrue(true); // if we reach this, everything is ok
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drawerScreenshot() {
|
||||
ActivityScenario.launch(FileDisplayActivity.class);
|
||||
|
||||
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
|
||||
|
||||
Screengrab.screenshot("03_drawer");
|
||||
|
||||
onView(withId(R.id.drawer_layout)).perform(DrawerActions.close());
|
||||
|
||||
Assert.assertTrue(true); // if we reach this, everything is ok
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleAccountsScreenshot() {
|
||||
ActivityScenario.launch(FileDisplayActivity.class);
|
||||
|
||||
onView(withId(R.id.switch_account_button)).perform(click());
|
||||
|
||||
Screengrab.screenshot("04_accounts");
|
||||
|
||||
pressBack();
|
||||
|
||||
Assert.assertTrue(true); // if we reach this, everything is ok
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoUploadScreenshot() {
|
||||
ActivityScenario.launch(SyncedFoldersActivity.class);
|
||||
|
||||
Screengrab.screenshot("05_autoUpload");
|
||||
|
||||
Assert.assertTrue(true); // if we reach this, everything is ok
|
||||
}
|
||||
|
||||
@Test
|
||||
public void davdroidScreenshot() {
|
||||
ActivityScenario.launch(SettingsActivity.class);
|
||||
|
||||
onData(PreferenceMatchers.withTitle(R.string.prefs_category_more)).perform(ViewActions.scrollTo());
|
||||
|
||||
shortSleep();
|
||||
|
||||
Screengrab.screenshot("06_davdroid");
|
||||
|
||||
Assert.assertTrue(true); // if we reach this, everything is ok
|
||||
}
|
||||
}
|
||||
517
app/src/androidTest/java/com/owncloud/android/UploadIT.java
Normal file
517
app/src/androidTest/java/com/owncloud/android/UploadIT.java
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android;
|
||||
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.nextcloud.client.device.BatteryStatus;
|
||||
import com.nextcloud.client.device.PowerManagementService;
|
||||
import com.nextcloud.client.jobs.upload.FileUploadWorker;
|
||||
import com.nextcloud.client.network.Connectivity;
|
||||
import com.nextcloud.client.network.ConnectivityService;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.resources.files.model.GeoLocation;
|
||||
import com.owncloud.android.lib.resources.files.model.ImageDimension;
|
||||
import com.owncloud.android.lib.resources.status.NextcloudVersion;
|
||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests related to file uploads.
|
||||
*/
|
||||
public class UploadIT extends AbstractOnServerIT {
|
||||
private static final String FOLDER = "/testUpload/";
|
||||
|
||||
private UploadsStorageManager uploadsStorageManager =
|
||||
new UploadsStorageManager(UserAccountManagerImpl.fromContext(targetContext),
|
||||
targetContext.getContentResolver());
|
||||
|
||||
private ConnectivityService connectivityServiceMock = new ConnectivityService() {
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternetWalled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connectivity getConnectivity() {
|
||||
return Connectivity.CONNECTED_WIFI;
|
||||
}
|
||||
};
|
||||
|
||||
private PowerManagementService powerManagementServiceMock = new PowerManagementService() {
|
||||
@Override
|
||||
public boolean isPowerSavingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingExclusionAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BatteryStatus getBattery() {
|
||||
return new BatteryStatus(false, 0);
|
||||
}
|
||||
};
|
||||
|
||||
@Before
|
||||
public void before() throws IOException {
|
||||
// make sure that every file is available, even after tests that remove source file
|
||||
createDummyFiles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyUpload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "empty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEmptyUpload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadWithCopy() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload, FileUploadWorker.LOCAL_BEHAVIOUR_COPY);
|
||||
|
||||
File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
|
||||
OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
|
||||
|
||||
assertTrue(originalFile.exists());
|
||||
assertTrue(new File(uploadedFile.getStoragePath()).exists());
|
||||
verifyStoragePath(uploadedFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadWithMove() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload, FileUploadWorker.LOCAL_BEHAVIOUR_MOVE);
|
||||
|
||||
File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
|
||||
OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
|
||||
|
||||
assertFalse(originalFile.exists());
|
||||
assertTrue(new File(uploadedFile.getStoragePath()).exists());
|
||||
verifyStoragePath(uploadedFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadWithForget() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload, FileUploadWorker.LOCAL_BEHAVIOUR_FORGET);
|
||||
|
||||
File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
|
||||
OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
|
||||
|
||||
assertTrue(originalFile.exists());
|
||||
assertFalse(new File(uploadedFile.getStoragePath()).exists());
|
||||
assertTrue(uploadedFile.getStoragePath().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadWithDelete() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
|
||||
FOLDER + "nonEmpty.txt",
|
||||
account.name);
|
||||
|
||||
uploadOCUpload(ocUpload, FileUploadWorker.LOCAL_BEHAVIOUR_DELETE);
|
||||
|
||||
File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
|
||||
OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
|
||||
|
||||
assertFalse(originalFile.exists());
|
||||
assertFalse(new File(uploadedFile.getStoragePath()).exists());
|
||||
assertTrue(uploadedFile.getStoragePath().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChunkedUpload() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/chunkedFile.txt",
|
||||
FOLDER + "chunkedFile.txt", account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadInNonExistingFolder() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "2/3/4/1.txt", account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadOnChargingOnlyButNotCharging() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "notCharging.txt", account.name);
|
||||
ocUpload.setWhileChargingOnly(true);
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
true,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertFalse(result.toString(), result.isSuccess());
|
||||
assertEquals(RemoteOperationResult.ResultCode.DELAYED_FOR_CHARGING, result.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadOnChargingOnlyAndCharging() {
|
||||
PowerManagementService powerManagementServiceMock = new PowerManagementService() {
|
||||
@Override
|
||||
public boolean isPowerSavingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSavingExclusionAvailable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BatteryStatus getBattery() {
|
||||
return new BatteryStatus(true, 100);
|
||||
}
|
||||
};
|
||||
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "charging.txt", account.name);
|
||||
ocUpload.setWhileChargingOnly(true);
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
true,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadOnWifiOnlyButNoWifi() {
|
||||
ConnectivityService connectivityServiceMock = new ConnectivityService() {
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternetWalled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connectivity getConnectivity() {
|
||||
return new Connectivity(true, false, false, true);
|
||||
}
|
||||
};
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "noWifi.txt", account.name);
|
||||
ocUpload.setUseWifiOnly(true);
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
true,
|
||||
false,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertFalse(result.toString(), result.isSuccess());
|
||||
assertEquals(RemoteOperationResult.ResultCode.DELAYED_FOR_WIFI, result.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadOnWifiOnlyAndWifi() {
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "wifi.txt", account.name);
|
||||
ocUpload.setWhileChargingOnly(true);
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
true,
|
||||
false,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertTrue(result.toString(), result.isSuccess());
|
||||
|
||||
// cleanup
|
||||
new RemoveFileOperation(getStorageManager().getFileByDecryptedRemotePath(FOLDER),
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadOnWifiOnlyButMeteredWifi() {
|
||||
ConnectivityService connectivityServiceMock = new ConnectivityService() {
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternetWalled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connectivity getConnectivity() {
|
||||
return new Connectivity(true, true, true, true);
|
||||
}
|
||||
};
|
||||
OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
|
||||
FOLDER + "noWifi.txt",
|
||||
account.name);
|
||||
ocUpload.setUseWifiOnly(true);
|
||||
|
||||
UploadFileOperation newUpload = new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
true,
|
||||
false,
|
||||
getStorageManager()
|
||||
);
|
||||
newUpload.setRemoteFolderToBeCreated();
|
||||
newUpload.addRenameUploadListener(() -> {
|
||||
// dummy
|
||||
});
|
||||
|
||||
RemoteOperationResult result = newUpload.execute(client);
|
||||
assertFalse(result.toString(), result.isSuccess());
|
||||
assertEquals(RemoteOperationResult.ResultCode.DELAYED_FOR_WIFI, result.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreationAndUploadTimestamp() throws IOException, AccountUtils.AccountNotFoundException {
|
||||
testOnlyOnServer(NextcloudVersion.nextcloud_27);
|
||||
|
||||
File file = getDummyFile("empty.txt");
|
||||
String remotePath = "/testFile.txt";
|
||||
OCUpload ocUpload = new OCUpload(file.getAbsolutePath(), remotePath, account.name);
|
||||
|
||||
assertTrue(
|
||||
new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
getStorageManager()
|
||||
)
|
||||
.setRemoteFolderToBeCreated()
|
||||
.execute(client)
|
||||
.isSuccess()
|
||||
);
|
||||
|
||||
long creationTimestamp = Files.readAttributes(file.toPath(), BasicFileAttributes.class)
|
||||
.creationTime()
|
||||
.to(TimeUnit.SECONDS);
|
||||
|
||||
long uploadTimestamp = System.currentTimeMillis() / 1000;
|
||||
|
||||
// RefreshFolderOperation
|
||||
assertTrue(new RefreshFolderOperation(getStorageManager().getFileByDecryptedRemotePath("/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
getStorageManager(),
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
List<OCFile> files = getStorageManager().getFolderContent(getStorageManager().getFileByDecryptedRemotePath("/"),
|
||||
false);
|
||||
|
||||
OCFile ocFile = files.get(0);
|
||||
|
||||
assertEquals(remotePath, ocFile.getRemotePath());
|
||||
assertEquals(creationTimestamp, ocFile.getCreationTimestamp());
|
||||
assertTrue(uploadTimestamp - 10 < ocFile.getUploadTimestamp() ||
|
||||
uploadTimestamp + 10 > ocFile.getUploadTimestamp());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetadata() throws IOException, AccountUtils.AccountNotFoundException {
|
||||
testOnlyOnServer(NextcloudVersion.nextcloud_27);
|
||||
|
||||
File file = getFile("gps.jpg");
|
||||
String remotePath = "/metadata.jpg";
|
||||
OCUpload ocUpload = new OCUpload(file.getAbsolutePath(), remotePath, account.name);
|
||||
|
||||
assertTrue(
|
||||
new UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
getStorageManager()
|
||||
)
|
||||
.setRemoteFolderToBeCreated()
|
||||
.execute(client)
|
||||
.isSuccess()
|
||||
);
|
||||
|
||||
// RefreshFolderOperation
|
||||
assertTrue(new RefreshFolderOperation(getStorageManager().getFileByDecryptedRemotePath("/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
getStorageManager(),
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
List<OCFile> files = getStorageManager().getFolderContent(getStorageManager().getFileByDecryptedRemotePath("/"),
|
||||
false);
|
||||
|
||||
OCFile ocFile = null;
|
||||
for (OCFile f : files) {
|
||||
if (f.getFileName().equals("metadata.jpg")) {
|
||||
ocFile = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(ocFile);
|
||||
assertEquals(remotePath, ocFile.getRemotePath());
|
||||
assertEquals(new GeoLocation(64, -46), ocFile.getGeoLocation());
|
||||
assertEquals(new ImageDimension(300f, 200f), ocFile.getImageDimension());
|
||||
}
|
||||
|
||||
private void verifyStoragePath(OCFile file) {
|
||||
assertEquals(FileStorageUtils.getSavePath(account.name) + FOLDER + file.getDecryptedFileName(),
|
||||
file.getStoragePath());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.authentication
|
||||
|
||||
import android.graphics.Color
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class AuthenticatorActivityIT {
|
||||
@Test(expected = IndexOutOfBoundsException::class)
|
||||
fun testException() {
|
||||
Color.parseColor("")
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
fun tryCatch() {
|
||||
val color = try {
|
||||
Color.parseColor("1")
|
||||
} catch (e: Exception) {
|
||||
Color.BLACK
|
||||
}
|
||||
|
||||
Assert.assertNotNull(color)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
fun tryCatch2() {
|
||||
val color = try {
|
||||
Color.parseColor("")
|
||||
} catch (e: Exception) {
|
||||
Color.BLACK
|
||||
}
|
||||
|
||||
Assert.assertNotNull(color)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
fun tryCatch3() {
|
||||
val color = try {
|
||||
Color.parseColor(null)
|
||||
} catch (e: Exception) {
|
||||
Color.BLACK
|
||||
}
|
||||
|
||||
Assert.assertNotNull(color)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
fun tryCatch4() {
|
||||
val color = try {
|
||||
Color.parseColor("abc")
|
||||
} catch (e: Exception) {
|
||||
Color.BLACK
|
||||
}
|
||||
|
||||
Assert.assertNotNull(color)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.authentication
|
||||
|
||||
import androidx.test.core.app.launchActivity
|
||||
import com.nextcloud.client.core.Clock
|
||||
import com.nextcloud.client.preferences.AppPreferences
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.ui.activity.SettingsActivity
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* This class should really be unit tests, but PassCodeManager needs a refactor
|
||||
* to decouple the locking logic from the platform classes
|
||||
*/
|
||||
class PassCodeManagerIT {
|
||||
@MockK
|
||||
lateinit var appPreferences: AppPreferences
|
||||
|
||||
@MockK
|
||||
lateinit var clockImpl: Clock
|
||||
|
||||
lateinit var sut: PassCodeManager
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
MockKAnnotations.init(this, relaxed = true)
|
||||
sut = PassCodeManager(appPreferences, clockImpl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testResumeDuplicateActivity() {
|
||||
// set locked state
|
||||
every { appPreferences.lockPreference } returns SettingsActivity.LOCK_PASSCODE
|
||||
every { appPreferences.lockTimestamp } returns 200
|
||||
every { clockImpl.millisSinceBoot } returns 10000
|
||||
|
||||
launchActivity<TestActivity>().use { scenario ->
|
||||
scenario.onActivity { activity ->
|
||||
// resume activity twice
|
||||
var askedForPin = sut.onActivityResumed(activity)
|
||||
assertTrue("Passcode not requested on first launch", askedForPin)
|
||||
sut.onActivityResumed(activity)
|
||||
|
||||
// stop it once
|
||||
sut.onActivityStopped(activity)
|
||||
|
||||
// resume again. should ask for passcode
|
||||
askedForPin = sut.onActivityResumed(activity)
|
||||
assertTrue("Passcode not requested on subsequent launch after stop", askedForPin)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ArbitraryDataProviderIT : AbstractIT() {
|
||||
|
||||
@Test
|
||||
fun testEmpty() {
|
||||
val key = "DUMMY_KEY"
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, "")
|
||||
|
||||
assertEquals("", arbitraryDataProvider.getValue(user.accountName, key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testString() {
|
||||
val key = "DUMMY_KEY"
|
||||
var value = "123"
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
|
||||
assertEquals(value, arbitraryDataProvider.getValue(user.accountName, key))
|
||||
|
||||
value = ""
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
|
||||
assertEquals(value, arbitraryDataProvider.getValue(user.accountName, key))
|
||||
|
||||
value = "-1"
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
|
||||
assertEquals(value, arbitraryDataProvider.getValue(user.accountName, key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBoolean() {
|
||||
val key = "DUMMY_KEY"
|
||||
var value = true
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
|
||||
assertEquals(value, arbitraryDataProvider.getBooleanValue(user.accountName, key))
|
||||
|
||||
value = false
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value)
|
||||
assertEquals(value, arbitraryDataProvider.getBooleanValue(user.accountName, key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInteger() {
|
||||
val key = "DUMMY_KEY"
|
||||
var value = 1
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value.toString())
|
||||
assertEquals(value, arbitraryDataProvider.getIntegerValue(user.accountName, key))
|
||||
|
||||
value = -1
|
||||
arbitraryDataProvider.storeOrUpdateKeyValue(user.accountName, key, value.toString())
|
||||
assertEquals(value, arbitraryDataProvider.getIntegerValue(user.accountName, key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIncrement() {
|
||||
val key = "INCREMENT"
|
||||
|
||||
// key does not exist
|
||||
assertEquals(-1, arbitraryDataProvider.getIntegerValue(user.accountName, key))
|
||||
|
||||
// increment -> 1
|
||||
arbitraryDataProvider.incrementValue(user.accountName, key)
|
||||
assertEquals(1, arbitraryDataProvider.getIntegerValue(user.accountName, key))
|
||||
|
||||
// increment -> 2
|
||||
arbitraryDataProvider.incrementValue(user.accountName, key)
|
||||
assertEquals(2, arbitraryDataProvider.getIntegerValue(user.accountName, key))
|
||||
|
||||
// delete
|
||||
arbitraryDataProvider.deleteKeyForAccount(user.accountName, key)
|
||||
assertEquals(-1, arbitraryDataProvider.getIntegerValue(user.accountName, key))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SdkSuppress
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.argThat
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.verify
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ContentResolverHelperIT {
|
||||
|
||||
companion object {
|
||||
private val URI = Uri.parse("http://foo.bar")
|
||||
private val PROJECTION = arrayOf("Foo")
|
||||
private const val SELECTION = "selection"
|
||||
private const val SORT_COLUMN = "sortColumn"
|
||||
private const val SORT_DIRECTION = ContentResolverHelper.SORT_DIRECTION_ASCENDING
|
||||
private const val SORT_DIRECTION_INT = ContentResolver.QUERY_SORT_DIRECTION_ASCENDING
|
||||
private const val LIMIT = 10
|
||||
}
|
||||
|
||||
@Mock
|
||||
lateinit var resolver: ContentResolver
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
|
||||
fun contentResolver_onAndroid26_usesNewAPI() {
|
||||
ContentResolverHelper
|
||||
.queryResolver(resolver, URI, PROJECTION, SELECTION, null, SORT_COLUMN, SORT_DIRECTION, LIMIT)
|
||||
|
||||
verify(resolver).query(
|
||||
eq(URI),
|
||||
eq(PROJECTION),
|
||||
argThat { bundle ->
|
||||
bundle.getString(ContentResolver.QUERY_ARG_SQL_SELECTION) == SELECTION &&
|
||||
bundle.getInt(ContentResolver.QUERY_ARG_LIMIT) == LIMIT &&
|
||||
bundle.getStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS)!!
|
||||
.contentEquals(arrayOf(SORT_COLUMN)) &&
|
||||
bundle.getInt(ContentResolver.QUERY_ARG_SORT_DIRECTION) == SORT_DIRECTION_INT
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.N_MR1)
|
||||
fun contentResolver_onAndroidBelow26_usesOldAPI() {
|
||||
ContentResolverHelper
|
||||
.queryResolver(resolver, URI, PROJECTION, SELECTION, null, SORT_COLUMN, SORT_DIRECTION, LIMIT)
|
||||
|
||||
verify(resolver).query(
|
||||
eq(URI),
|
||||
eq(PROJECTION),
|
||||
eq(SELECTION),
|
||||
eq(null),
|
||||
eq("$SORT_COLUMN $SORT_DIRECTION LIMIT $LIMIT"),
|
||||
eq(null)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
import com.owncloud.android.db.ProviderMeta;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class FileDataStorageManagerContentProviderClientIT extends FileDataStorageManagerIT {
|
||||
public void before() {
|
||||
sut = new FileDataStorageManager(user,
|
||||
targetContext
|
||||
.getContentResolver()
|
||||
.acquireContentProviderClient(ProviderMeta.ProviderTableMeta.CONTENT_URI)
|
||||
);
|
||||
|
||||
super.before();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saveFile() {
|
||||
|
||||
String path = "/1.txt";
|
||||
OCFile file = new OCFile(path);
|
||||
file.setRemoteId("00000008ocjycgrudn78");
|
||||
|
||||
// TODO check via reflection that every parameter is set
|
||||
|
||||
file.setFileLength(1024000);
|
||||
file.setModificationTimestamp(1582019340);
|
||||
sut.saveNewFile(file);
|
||||
|
||||
|
||||
OCFile read = sut.getFileByPath(path);
|
||||
assertNotNull(read);
|
||||
|
||||
assertEquals(file.getRemotePath(), read.getRemotePath());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class FileDataStorageManagerContentResolverIT : FileDataStorageManagerIT() {
|
||||
companion object {
|
||||
private const val MANY_FILES_AMOUNT = 5000
|
||||
}
|
||||
|
||||
override fun before() {
|
||||
sut = FileDataStorageManager(user, targetContext.contentResolver)
|
||||
super.before()
|
||||
}
|
||||
|
||||
/**
|
||||
* only on FileDataStorageManager
|
||||
*/
|
||||
@Test
|
||||
fun testFolderWithManyFiles() {
|
||||
// create folder
|
||||
val folderA = OCFile("/folderA/")
|
||||
folderA.setFolder().parentId = sut.getFileByDecryptedRemotePath("/")!!.fileId
|
||||
sut.saveFile(folderA)
|
||||
Assert.assertTrue(sut.fileExists("/folderA/"))
|
||||
Assert.assertEquals(0, sut.getFolderContent(folderA, false).size)
|
||||
val folderAId = sut.getFileByDecryptedRemotePath("/folderA/")!!.fileId
|
||||
|
||||
// create files
|
||||
val newFiles = (1..MANY_FILES_AMOUNT).map {
|
||||
val file = OCFile("/folderA/file$it")
|
||||
file.parentId = folderAId
|
||||
sut.saveFile(file)
|
||||
|
||||
val storedFile = sut.getFileByDecryptedRemotePath("/folderA/file$it")
|
||||
Assert.assertNotNull(storedFile)
|
||||
storedFile
|
||||
}
|
||||
|
||||
// save files in folder
|
||||
sut.saveFolder(
|
||||
folderA,
|
||||
newFiles,
|
||||
ArrayList()
|
||||
)
|
||||
// check file count is correct
|
||||
Assert.assertEquals(MANY_FILES_AMOUNT, sut.getFolderContent(folderA, false).size)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import com.owncloud.android.AbstractOnServerIT;
|
||||
import com.owncloud.android.db.ProviderMeta;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.SearchRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile;
|
||||
import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
|
||||
import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation;
|
||||
import com.owncloud.android.lib.resources.status.OCCapability;
|
||||
import com.owncloud.android.operations.RefreshFolderOperation;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.owncloud.android.lib.resources.files.SearchRemoteOperation.SearchType.GALLERY_SEARCH;
|
||||
import static com.owncloud.android.lib.resources.files.SearchRemoteOperation.SearchType.PHOTO_SEARCH;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
abstract public class FileDataStorageManagerIT extends AbstractOnServerIT {
|
||||
|
||||
protected FileDataStorageManager sut;
|
||||
private OCCapability capability;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
// make sure everything is removed
|
||||
sut.deleteAllFiles();
|
||||
sut.deleteVirtuals(VirtualFolderType.GALLERY);
|
||||
|
||||
assertEquals(0, sut.getAllFiles().size());
|
||||
|
||||
capability = (OCCapability) new GetCapabilitiesRemoteOperation(null)
|
||||
.execute(client)
|
||||
.getSingleData();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
super.after();
|
||||
|
||||
sut.deleteAllFiles();
|
||||
sut.deleteVirtuals(VirtualFolderType.GALLERY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleTest() {
|
||||
OCFile file = sut.getFileByDecryptedRemotePath("/");
|
||||
assertNotNull(file);
|
||||
assertTrue(file.fileExists());
|
||||
assertNull(sut.getFileByDecryptedRemotePath("/123123"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAllFiles_NoAvailable() {
|
||||
assertEquals(0, sut.getAllFiles().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFolderContent() throws IOException {
|
||||
assertEquals(0, sut.getAllFiles().size());
|
||||
assertTrue(new CreateFolderRemoteOperation("/1/1/", true).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new CreateFolderRemoteOperation("/1/2/", true).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new UploadFileRemoteOperation(getDummyFile("chunkedFile.txt").getAbsolutePath(),
|
||||
"/1/1/chunkedFile.txt",
|
||||
"text/plain",
|
||||
System.currentTimeMillis() / 1000)
|
||||
.execute(client).isSuccess());
|
||||
|
||||
assertTrue(new UploadFileRemoteOperation(getDummyFile("chunkedFile.txt").getAbsolutePath(),
|
||||
"/1/1/chunkedFile2.txt",
|
||||
"text/plain",
|
||||
System.currentTimeMillis() / 1000)
|
||||
.execute(client).isSuccess());
|
||||
|
||||
File imageFile = getFile("imageFile.png");
|
||||
assertTrue(new UploadFileRemoteOperation(imageFile.getAbsolutePath(),
|
||||
"/1/1/imageFile.png",
|
||||
"image/png",
|
||||
System.currentTimeMillis() / 1000)
|
||||
.execute(client).isSuccess());
|
||||
|
||||
// sync
|
||||
assertNull(sut.getFileByDecryptedRemotePath("/1/1/"));
|
||||
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/1/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/1/1/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
assertEquals(3, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/1/1/"), false).size());
|
||||
}
|
||||
|
||||
/**
|
||||
* This test creates an image, does a photo search (now returned image is not yet in file hierarchy), then root
|
||||
* folder is refreshed and it is verified that the same image file is used in database
|
||||
*/
|
||||
@Test
|
||||
public void testPhotoSearch() throws IOException {
|
||||
String remotePath = "/imageFile.png";
|
||||
VirtualFolderType virtualType = VirtualFolderType.GALLERY;
|
||||
|
||||
assertEquals(0, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).size());
|
||||
assertEquals(1, sut.getAllFiles().size());
|
||||
|
||||
File imageFile = getFile("imageFile.png");
|
||||
assertTrue(new UploadFileRemoteOperation(imageFile.getAbsolutePath(),
|
||||
remotePath,
|
||||
"image/png",
|
||||
System.currentTimeMillis() / 1000)
|
||||
.execute(client).isSuccess());
|
||||
|
||||
assertNull(sut.getFileByDecryptedRemotePath(remotePath));
|
||||
|
||||
// search
|
||||
SearchRemoteOperation searchRemoteOperation = new SearchRemoteOperation("image/%",
|
||||
PHOTO_SEARCH,
|
||||
false,
|
||||
capability);
|
||||
|
||||
RemoteOperationResult<List<RemoteFile>> searchResult = searchRemoteOperation.execute(client);
|
||||
TestCase.assertTrue(searchResult.isSuccess());
|
||||
TestCase.assertEquals(1, searchResult.getResultData().size());
|
||||
|
||||
OCFile ocFile = FileStorageUtils.fillOCFile(searchResult.getResultData().get(0));
|
||||
sut.saveFile(ocFile);
|
||||
|
||||
List<ContentValues> contentValues = new ArrayList<>();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_TYPE, virtualType.toString());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_OCFILE_ID, ocFile.getFileId());
|
||||
|
||||
contentValues.add(cv);
|
||||
|
||||
sut.saveVirtuals(contentValues);
|
||||
|
||||
assertEquals(remotePath, ocFile.getRemotePath());
|
||||
|
||||
assertEquals(0, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).size());
|
||||
|
||||
assertEquals(1, sut.getVirtualFolderContent(virtualType, false).size());
|
||||
assertEquals(2, sut.getAllFiles().size());
|
||||
|
||||
// update root
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
|
||||
assertEquals(1, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).size());
|
||||
assertEquals(1, sut.getVirtualFolderContent(virtualType, false).size());
|
||||
assertEquals(2, sut.getAllFiles().size());
|
||||
|
||||
assertEquals(sut.getVirtualFolderContent(virtualType, false).get(0),
|
||||
sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* This test creates an image and a video, does a gallery search (now returned image and video is not yet in file
|
||||
* hierarchy), then root folder is refreshed and it is verified that the same image file is used in database
|
||||
*/
|
||||
@Test
|
||||
public void testGallerySearch() throws IOException {
|
||||
sut = new FileDataStorageManager(user,
|
||||
targetContext
|
||||
.getContentResolver()
|
||||
.acquireContentProviderClient(ProviderMeta.ProviderTableMeta.CONTENT_URI)
|
||||
);
|
||||
|
||||
String imagePath = "/imageFile.png";
|
||||
VirtualFolderType virtualType = VirtualFolderType.GALLERY;
|
||||
|
||||
assertEquals(0, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).size());
|
||||
assertEquals(1, sut.getAllFiles().size());
|
||||
|
||||
File imageFile = getFile("imageFile.png");
|
||||
assertTrue(new UploadFileRemoteOperation(imageFile.getAbsolutePath(),
|
||||
imagePath,
|
||||
"image/png",
|
||||
(System.currentTimeMillis() - 10000) / 1000)
|
||||
.execute(client).isSuccess());
|
||||
|
||||
// Check that file does not yet exist in local database
|
||||
assertNull(sut.getFileByDecryptedRemotePath(imagePath));
|
||||
|
||||
String videoPath = "/videoFile.mp4";
|
||||
File videoFile = getFile("videoFile.mp4");
|
||||
assertTrue(new UploadFileRemoteOperation(videoFile.getAbsolutePath(),
|
||||
videoPath,
|
||||
"video/mpeg",
|
||||
(System.currentTimeMillis() + 10000) / 1000)
|
||||
.execute(client).isSuccess());
|
||||
|
||||
// Check that file does not yet exist in local database
|
||||
assertNull(sut.getFileByDecryptedRemotePath(videoPath));
|
||||
|
||||
// search
|
||||
SearchRemoteOperation searchRemoteOperation = new SearchRemoteOperation("",
|
||||
GALLERY_SEARCH,
|
||||
false,
|
||||
capability);
|
||||
|
||||
RemoteOperationResult<List<RemoteFile>> searchResult = searchRemoteOperation.execute(client);
|
||||
TestCase.assertTrue(searchResult.isSuccess());
|
||||
TestCase.assertEquals(2, searchResult.getResultData().size());
|
||||
|
||||
// newest file must be video path (as sorted by recently modified)
|
||||
OCFile ocFile = FileStorageUtils.fillOCFile( searchResult.getResultData().get(0));
|
||||
sut.saveFile(ocFile);
|
||||
assertEquals(videoPath, ocFile.getRemotePath());
|
||||
|
||||
List<ContentValues> contentValues = new ArrayList<>();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_TYPE, virtualType.toString());
|
||||
cv.put(ProviderMeta.ProviderTableMeta.VIRTUAL_OCFILE_ID, ocFile.getFileId());
|
||||
|
||||
contentValues.add(cv);
|
||||
|
||||
// second is image file, as older
|
||||
OCFile ocFile2 = FileStorageUtils.fillOCFile(searchResult.getResultData().get(1));
|
||||
sut.saveFile(ocFile2);
|
||||
assertEquals(imagePath, ocFile2.getRemotePath());
|
||||
|
||||
ContentValues cv2 = new ContentValues();
|
||||
cv2.put(ProviderMeta.ProviderTableMeta.VIRTUAL_TYPE, virtualType.toString());
|
||||
cv2.put(ProviderMeta.ProviderTableMeta.VIRTUAL_OCFILE_ID, ocFile2.getFileId());
|
||||
|
||||
contentValues.add(cv2);
|
||||
|
||||
sut.saveVirtuals(contentValues);
|
||||
|
||||
assertEquals(0, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).size());
|
||||
|
||||
assertEquals(2, sut.getVirtualFolderContent(virtualType, false).size());
|
||||
assertEquals(3, sut.getAllFiles().size());
|
||||
|
||||
// update root
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
|
||||
assertEquals(2, sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).size());
|
||||
assertEquals(2, sut.getVirtualFolderContent(virtualType, false).size());
|
||||
assertEquals(3, sut.getAllFiles().size());
|
||||
|
||||
assertEquals(sut.getVirtualFolderContent(virtualType, false).get(0),
|
||||
sut.getFolderContent(sut.getFileByDecryptedRemotePath("/"), false).get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveNewFile() {
|
||||
assertTrue(new CreateFolderRemoteOperation("/1/1/", true).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/1/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
assertTrue(new RefreshFolderOperation(sut.getFileByDecryptedRemotePath("/1/1/"),
|
||||
System.currentTimeMillis() / 1000,
|
||||
false,
|
||||
false,
|
||||
sut,
|
||||
user,
|
||||
targetContext).execute(client).isSuccess());
|
||||
|
||||
OCFile newFile = new OCFile("/1/1/1.txt");
|
||||
newFile.setRemoteId("12345678");
|
||||
|
||||
sut.saveNewFile(newFile);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testSaveNewFile_NonExistingParent() {
|
||||
assertTrue(new CreateFolderRemoteOperation("/1/1/", true).execute(client).isSuccess());
|
||||
|
||||
OCFile newFile = new OCFile("/1/1/1.txt");
|
||||
|
||||
sut.saveNewFile(newFile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOCCapability() {
|
||||
OCCapability capability = new OCCapability();
|
||||
capability.setUserStatus(CapabilityBooleanType.TRUE);
|
||||
|
||||
sut.saveCapabilities(capability);
|
||||
|
||||
OCCapability newCapability = sut.getCapability(user);
|
||||
|
||||
assertEquals(capability.getUserStatus(), newCapability.getUserStatus());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.lib.resources.status.CapabilityBooleanType
|
||||
import com.owncloud.android.lib.resources.status.OCCapability
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class OCCapabilityIT : AbstractIT() {
|
||||
@Test
|
||||
fun saveCapability() {
|
||||
val fileDataStorageManager = FileDataStorageManager(user, targetContext.contentResolver)
|
||||
|
||||
val capability = OCCapability()
|
||||
capability.etag = "123"
|
||||
capability.userStatus = CapabilityBooleanType.TRUE
|
||||
capability.userStatusSupportsEmoji = CapabilityBooleanType.TRUE
|
||||
capability.dropAccount = CapabilityBooleanType.TRUE
|
||||
|
||||
fileDataStorageManager.saveCapabilities(capability)
|
||||
|
||||
val newCapability = fileDataStorageManager.getCapability(user.accountName)
|
||||
|
||||
assertEquals(capability.etag, newCapability.etag)
|
||||
assertEquals(capability.userStatus, newCapability.userStatus)
|
||||
assertEquals(capability.userStatusSupportsEmoji, newCapability.userStatusSupportsEmoji)
|
||||
assertEquals(capability.dropAccount, newCapability.dropAccount)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper_ozturk@proton.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel
|
||||
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.lib.common.network.WebdavEntry.MountType
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class OCFileIconTests {
|
||||
|
||||
private val path = "/path/to/a/file.txt"
|
||||
private var sut: OCFile? = null
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
sut = OCFile(path)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsAutoUploadFolderShouldReturnFolderOverlayUploadIcon() {
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(true)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_upload
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsEncryptedShouldReturnFolderOverlayKeyIcon() {
|
||||
sut?.isEncrypted = true
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_key
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsGroupFolderShouldReturnFolderOverlayAccountGroupIcon() {
|
||||
sut?.mountType = MountType.GROUP
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_account_group
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsSharedViaLinkShouldReturnFolderOverlayLinkIcon() {
|
||||
sut?.isSharedViaLink = true
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_link
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsSharedShouldReturnFolderOverlayShareIcon() {
|
||||
sut?.isSharedWithSharee = true
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_share
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsExternalShouldReturnFolderOverlayExternalIcon() {
|
||||
sut?.mountType = MountType.EXTERNAL
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_external
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsLockedShouldReturnFolderOverlayLockIcon() {
|
||||
sut?.isLocked = true
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
val expectedDrawable = R.drawable.ic_folder_overlay_lock
|
||||
assert(fileOverlayIcon == expectedDrawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFileOverlayIconWhenFileIsFolderShouldReturnNull() {
|
||||
val fileOverlayIcon = sut?.getFileOverlayIconId(false)
|
||||
assert(fileOverlayIcon == null)
|
||||
}
|
||||
|
||||
@After
|
||||
fun destroy() {
|
||||
sut = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2016 David A. Velasco
|
||||
* SPDX-FileCopyrightText: 2016 2016 ownCloud Inc
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
|
||||
/**
|
||||
* Instrumented unit test, to be run in an Android emulator or device.
|
||||
* At the moment, it's a sample to validate the automatic test environment, in the scope of instrumented unit tests.
|
||||
* Don't take it as an example of completeness.
|
||||
* See http://developer.android.com/intl/es/training/testing/unit-testing/instrumented-unit-tests.html .
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class OCFileUnitTest {
|
||||
|
||||
private final static String PATH = "/path/to/a/file.txt";
|
||||
private static final long ID = 12345L;
|
||||
private static final long PARENT_ID = 567890L;
|
||||
private static final String STORAGE_PATH = "/mnt/sd/localpath/to/a/file.txt";
|
||||
private static final String MIME_TYPE = "text/plain";
|
||||
private static final long FILE_LENGTH = 9876543210L;
|
||||
private static final long CREATION_TIMESTAMP = 8765432109L;
|
||||
private static final long MODIFICATION_TIMESTAMP = 7654321098L;
|
||||
private static final long MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA = 6543210987L;
|
||||
private static final long LAST_SYNC_DATE_FOR_PROPERTIES = 5432109876L;
|
||||
private static final long LAST_SYNC_DATE_FOR_DATA = 4321098765L;
|
||||
private static final String ETAG = "adshfas98ferqw8f9yu2";
|
||||
private static final String PUBLIC_LINK = "https://nextcloud.localhost/owncloud/987427448712984sdas29";
|
||||
private static final String PERMISSIONS = "SRKNVD";
|
||||
private static final String REMOTE_ID = "jadñgiadf8203:9jrp98v2mn3er2089fh";
|
||||
private static final String ETAG_IN_CONFLICT = "2adshfas98ferqw8f9yu";
|
||||
|
||||
private OCFile mFile;
|
||||
|
||||
@Before
|
||||
public void createDefaultOCFile() {
|
||||
mFile = new OCFile(PATH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeThenReadAsParcelable() {
|
||||
|
||||
// Set up mFile with not-default values
|
||||
mFile.setFileId(ID);
|
||||
mFile.setParentId(PARENT_ID);
|
||||
mFile.setStoragePath(STORAGE_PATH);
|
||||
mFile.setMimeType(MIME_TYPE);
|
||||
mFile.setFileLength(FILE_LENGTH);
|
||||
mFile.setCreationTimestamp(CREATION_TIMESTAMP);
|
||||
mFile.setModificationTimestamp(MODIFICATION_TIMESTAMP);
|
||||
mFile.setModificationTimestampAtLastSyncForData(MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA);
|
||||
mFile.setLastSyncDateForProperties(LAST_SYNC_DATE_FOR_PROPERTIES);
|
||||
mFile.setLastSyncDateForData(LAST_SYNC_DATE_FOR_DATA);
|
||||
mFile.setEtag(ETAG);
|
||||
mFile.setSharedViaLink(true);
|
||||
mFile.setSharedWithSharee(true);
|
||||
mFile.setPermissions(PERMISSIONS);
|
||||
mFile.setRemoteId(REMOTE_ID);
|
||||
mFile.setUpdateThumbnailNeeded(true);
|
||||
mFile.setDownloading(true);
|
||||
mFile.setEtagInConflict(ETAG_IN_CONFLICT);
|
||||
|
||||
|
||||
// Write the file data in a Parcel
|
||||
Parcel parcel = Parcel.obtain();
|
||||
mFile.writeToParcel(parcel, mFile.describeContents());
|
||||
|
||||
// Read the data from the parcel
|
||||
parcel.setDataPosition(0);
|
||||
OCFile fileReadFromParcel = OCFile.CREATOR.createFromParcel(parcel);
|
||||
|
||||
// Verify that the received data are correct
|
||||
assertThat(fileReadFromParcel.getRemotePath(), is(PATH));
|
||||
assertThat(fileReadFromParcel.getFileId(), is(ID));
|
||||
assertThat(fileReadFromParcel.getParentId(), is(PARENT_ID));
|
||||
assertThat(fileReadFromParcel.getStoragePath(), is(STORAGE_PATH));
|
||||
assertThat(fileReadFromParcel.getMimeType(), is(MIME_TYPE));
|
||||
assertThat(fileReadFromParcel.getFileLength(), is(FILE_LENGTH));
|
||||
assertThat(fileReadFromParcel.getCreationTimestamp(), is(CREATION_TIMESTAMP));
|
||||
assertThat(fileReadFromParcel.getModificationTimestamp(), is(MODIFICATION_TIMESTAMP));
|
||||
assertThat(
|
||||
fileReadFromParcel.getModificationTimestampAtLastSyncForData(),
|
||||
is(MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA)
|
||||
);
|
||||
assertThat(fileReadFromParcel.getLastSyncDateForProperties(), is(LAST_SYNC_DATE_FOR_PROPERTIES));
|
||||
assertThat(fileReadFromParcel.getLastSyncDateForData(), is(LAST_SYNC_DATE_FOR_DATA));
|
||||
assertThat(fileReadFromParcel.getEtag(), is(ETAG));
|
||||
assertThat(fileReadFromParcel.isSharedViaLink(), is(true));
|
||||
assertThat(fileReadFromParcel.isSharedWithSharee(), is(true));
|
||||
assertThat(fileReadFromParcel.getPermissions(), is(PERMISSIONS));
|
||||
assertThat(fileReadFromParcel.getRemoteId(), is(REMOTE_ID));
|
||||
assertThat(fileReadFromParcel.isUpdateThumbnailNeeded(), is(true));
|
||||
assertThat(fileReadFromParcel.isDownloading(), is(true));
|
||||
assertThat(fileReadFromParcel.getEtagInConflict(), is(ETAG_IN_CONFLICT));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2017 JARP <jarp@customer-187-174-218-184.uninet-ide.com.mx
|
||||
* SPDX-FileCopyrightText: 2019 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2021 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.datamodel;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
|
||||
import com.nextcloud.client.account.CurrentAccountProvider;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.nextcloud.test.RandomStringGenerator;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.db.UploadResult;
|
||||
import com.owncloud.android.files.services.NameCollisionPolicy;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.operations.UploadFileOperation;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
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.assertTrue;
|
||||
|
||||
/**
|
||||
* Created by JARP on 6/7/17.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class UploadStorageManagerTest extends AbstractIT {
|
||||
private UploadsStorageManager uploadsStorageManager;
|
||||
private CurrentAccountProvider currentAccountProvider = () -> null;
|
||||
private UserAccountManager userAccountManager;
|
||||
private User user2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Context instrumentationCtx = ApplicationProvider.getApplicationContext();
|
||||
ContentResolver contentResolver = instrumentationCtx.getContentResolver();
|
||||
uploadsStorageManager = new UploadsStorageManager(currentAccountProvider, contentResolver);
|
||||
userAccountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
|
||||
Account temp = new Account("test2@test.com", MainApp.getAccountType(targetContext));
|
||||
if (!userAccountManager.exists(temp)) {
|
||||
AccountManager platformAccountManager = AccountManager.get(targetContext);
|
||||
platformAccountManager.addAccountExplicitly(temp, "testPassword", null);
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION,
|
||||
Integer.toString(UserAccountManager.ACCOUNT_VERSION));
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0");
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "test.com");
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "test"); // same as userId
|
||||
}
|
||||
|
||||
final UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
user2 = userAccountManager.getUser("test2@test.com").orElseThrow(ActivityNotFoundException::new);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteAllUploads() {
|
||||
// Clean
|
||||
for (User user : userAccountManager.getAllUsers()) {
|
||||
uploadsStorageManager.removeUserUploads(user);
|
||||
}
|
||||
int accountRowsA = 3;
|
||||
int accountRowsB = 4;
|
||||
insertUploads(account, accountRowsA);
|
||||
insertUploads(user2.toPlatformAccount(), accountRowsB);
|
||||
|
||||
assertEquals("Expected 4 removed uploads files",
|
||||
4,
|
||||
uploadsStorageManager.removeUserUploads(user2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void largeTest() {
|
||||
int size = 3000;
|
||||
ArrayList<OCUpload> uploads = new ArrayList<>();
|
||||
|
||||
deleteAllUploads();
|
||||
assertEquals(0, uploadsStorageManager.getAllStoredUploads().length);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
OCUpload upload = createUpload(account);
|
||||
|
||||
uploads.add(upload);
|
||||
uploadsStorageManager.storeUpload(upload);
|
||||
}
|
||||
|
||||
OCUpload[] storedUploads = uploadsStorageManager.getAllStoredUploads();
|
||||
assertEquals(size, uploadsStorageManager.getAllStoredUploads().length);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue(contains(uploads, storedUploads[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSame() {
|
||||
OCUpload upload1 = new OCUpload("/test", "/test", account.name);
|
||||
upload1.setUseWifiOnly(true);
|
||||
OCUpload upload2 = new OCUpload("/test", "/test", account.name);
|
||||
upload2.setUseWifiOnly(true);
|
||||
|
||||
assertTrue(upload1.isSame(upload2));
|
||||
|
||||
upload2.setUseWifiOnly(false);
|
||||
assertFalse(upload1.isSame(upload2));
|
||||
|
||||
assertFalse(upload1.isSame(null));
|
||||
assertFalse(upload1.isSame(new OCFile("/test")));
|
||||
}
|
||||
|
||||
private boolean contains(ArrayList<OCUpload> uploads, OCUpload storedUpload) {
|
||||
for (int i = 0; i < uploads.size(); i++) {
|
||||
if (storedUpload.isSame(uploads.get(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void corruptedUpload() {
|
||||
OCUpload corruptUpload = new OCUpload(File.separator + "LocalPath",
|
||||
OCFile.PATH_SEPARATOR + "RemotePath",
|
||||
account.name);
|
||||
|
||||
corruptUpload.setLocalPath(null);
|
||||
|
||||
uploadsStorageManager.storeUpload(corruptUpload);
|
||||
|
||||
uploadsStorageManager.getAllStoredUploads();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getById() {
|
||||
OCUpload upload = createUpload(account);
|
||||
long id = uploadsStorageManager.storeUpload(upload);
|
||||
|
||||
OCUpload newUpload = uploadsStorageManager.getUploadById(id);
|
||||
|
||||
assertNotNull(newUpload);
|
||||
assertEquals(upload.getLocalAction(), newUpload.getLocalAction());
|
||||
assertEquals(upload.getFolderUnlockToken(), newUpload.getFolderUnlockToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getByIdNull() {
|
||||
OCUpload newUpload = uploadsStorageManager.getUploadById(-1);
|
||||
|
||||
assertNull(newUpload);
|
||||
}
|
||||
|
||||
private void insertUploads(Account account, int rowsToInsert) {
|
||||
for (int i = 0; i < rowsToInsert; i++) {
|
||||
uploadsStorageManager.storeUpload(createUpload(account));
|
||||
}
|
||||
}
|
||||
|
||||
public String generateUniqueNumber() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
private OCUpload createUpload(Account account) {
|
||||
OCUpload upload = new OCUpload(File.separator + "very long long long long long long long long long long long " +
|
||||
"long long long long long long long long long long long long long long " +
|
||||
"long long long long long long long long long long long long long long " +
|
||||
"long long long long long long long LocalPath " +
|
||||
generateUniqueNumber(),
|
||||
OCFile.PATH_SEPARATOR + "very long long long long long long long long long " +
|
||||
"long long long long long long long long long long long long long long " +
|
||||
"long long long long long long long long long long long long long long " +
|
||||
"long long long long long long long long long long long long RemotePath " +
|
||||
generateUniqueNumber(),
|
||||
account.name);
|
||||
|
||||
upload.setFileSize(new Random().nextInt(20000) * 10000);
|
||||
upload.setUploadStatus(UploadsStorageManager.UploadStatus.UPLOAD_IN_PROGRESS);
|
||||
upload.setLocalAction(2);
|
||||
upload.setNameCollisionPolicy(NameCollisionPolicy.ASK_USER);
|
||||
upload.setCreateRemoteFolder(false);
|
||||
upload.setUploadEndTimestamp(System.currentTimeMillis());
|
||||
upload.setLastResult(UploadResult.DELAYED_FOR_WIFI);
|
||||
upload.setCreatedBy(UploadFileOperation.CREATED_BY_USER);
|
||||
upload.setUseWifiOnly(true);
|
||||
upload.setWhileChargingOnly(false);
|
||||
upload.setFolderUnlockToken(RandomStringGenerator.make(10));
|
||||
|
||||
return upload;
|
||||
}
|
||||
|
||||
private void deleteAllUploads() {
|
||||
uploadsStorageManager.removeAllUploads();
|
||||
|
||||
assertEquals(0, uploadsStorageManager.getAllStoredUploads().length);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
deleteAllUploads();
|
||||
userAccountManager.removeUser(user2);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey Vilas
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.files
|
||||
|
||||
import androidx.test.core.app.launchActivity
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.nextcloud.client.account.User
|
||||
import com.nextcloud.client.jobs.download.FileDownloadWorker
|
||||
import com.nextcloud.client.jobs.upload.FileUploadHelper
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.nextcloud.utils.EditorUtils
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.resources.files.model.FileLockType
|
||||
import com.owncloud.android.lib.resources.status.CapabilityBooleanType
|
||||
import com.owncloud.android.lib.resources.status.OCCapability
|
||||
import com.owncloud.android.services.OperationsService
|
||||
import com.owncloud.android.ui.activity.ComponentsGetter
|
||||
import com.owncloud.android.utils.MimeType
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.security.SecureRandom
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FileMenuFilterIT : AbstractIT() {
|
||||
|
||||
@MockK
|
||||
private lateinit var mockComponentsGetter: ComponentsGetter
|
||||
|
||||
@MockK
|
||||
private lateinit var mockStorageManager: FileDataStorageManager
|
||||
|
||||
@MockK
|
||||
private lateinit var mockFileUploaderBinder: FileUploadHelper
|
||||
|
||||
@MockK
|
||||
private lateinit var mockFileDownloadProgressListener: FileDownloadWorker.FileDownloadProgressListener
|
||||
|
||||
@MockK
|
||||
private lateinit var mockOperationsServiceBinder: OperationsService.OperationsServiceBinder
|
||||
|
||||
@MockK
|
||||
private lateinit var mockArbitraryDataProvider: ArbitraryDataProvider
|
||||
|
||||
private lateinit var editorUtils: EditorUtils
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
every { mockFileUploaderBinder.isUploading(any(), any()) } returns false
|
||||
every { mockComponentsGetter.fileUploaderHelper } returns mockFileUploaderBinder
|
||||
every { mockFileDownloadProgressListener.isDownloading(any(), any()) } returns false
|
||||
every { mockComponentsGetter.fileDownloadProgressListener } returns mockFileDownloadProgressListener
|
||||
every { mockOperationsServiceBinder.isSynchronizing(any(), any()) } returns false
|
||||
every { mockComponentsGetter.operationsServiceBinder } returns mockOperationsServiceBinder
|
||||
every { mockStorageManager.getFileById(any()) } returns OCFile("/")
|
||||
every { mockStorageManager.getFolderContent(any(), any()) } returns ArrayList<OCFile>()
|
||||
every { mockArbitraryDataProvider.getValue(any<User>(), any()) } returns ""
|
||||
editorUtils = EditorUtils(mockArbitraryDataProvider)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_noLockingCapability_lockItemsInvisible() {
|
||||
val capability = OCCapability().apply {
|
||||
endToEndEncryption = CapabilityBooleanType.UNKNOWN
|
||||
}
|
||||
|
||||
val file = OCFile("/foo.md")
|
||||
|
||||
testLockingVisibilities(
|
||||
capability,
|
||||
file,
|
||||
ExpectedLockVisibilities(lockFile = false, unlockFile = false)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_lockingCapability_fileUnlocked_lockVisible() {
|
||||
val capability = OCCapability().apply {
|
||||
endToEndEncryption = CapabilityBooleanType.UNKNOWN
|
||||
filesLockingVersion = "1.0"
|
||||
}
|
||||
|
||||
val file = OCFile("/foo.md")
|
||||
|
||||
testLockingVisibilities(
|
||||
capability,
|
||||
file,
|
||||
ExpectedLockVisibilities(lockFile = true, unlockFile = false)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_lockingCapability_fileLocked_lockedByAndProps() {
|
||||
val capability = OCCapability().apply {
|
||||
endToEndEncryption = CapabilityBooleanType.UNKNOWN
|
||||
filesLockingVersion = "1.0"
|
||||
}
|
||||
|
||||
val file = OCFile("/foo.md").apply {
|
||||
isLocked = true
|
||||
lockType = FileLockType.MANUAL
|
||||
lockOwnerId = user.accountName.split("@")[0]
|
||||
lockOwnerDisplayName = "TEST"
|
||||
lockTimestamp = 1000 // irrelevant
|
||||
lockTimeout = 1000 // irrelevant
|
||||
}
|
||||
|
||||
testLockingVisibilities(
|
||||
capability,
|
||||
file,
|
||||
ExpectedLockVisibilities(lockFile = false, unlockFile = true)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_lockingCapability_fileLockedByOthers_lockedByAndProps() {
|
||||
val capability = OCCapability().apply {
|
||||
endToEndEncryption = CapabilityBooleanType.UNKNOWN
|
||||
filesLockingVersion = "1.0"
|
||||
}
|
||||
|
||||
val file = OCFile("/foo.md").apply {
|
||||
isLocked = true
|
||||
lockType = FileLockType.MANUAL
|
||||
lockOwnerId = "A_DIFFERENT_USER"
|
||||
lockOwnerDisplayName = "A_DIFFERENT_USER"
|
||||
lockTimestamp = 1000 // irrelevant
|
||||
lockTimeout = 1000 // irrelevant
|
||||
}
|
||||
testLockingVisibilities(
|
||||
capability,
|
||||
file,
|
||||
ExpectedLockVisibilities(lockFile = false, unlockFile = false)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_unset_encryption() {
|
||||
val capability = OCCapability().apply {
|
||||
endToEndEncryption = CapabilityBooleanType.TRUE
|
||||
}
|
||||
|
||||
val encryptedFolder = OCFile("/encryptedFolder/").apply {
|
||||
isEncrypted = true
|
||||
mimeType = MimeType.DIRECTORY
|
||||
fileLength = SecureRandom().nextLong()
|
||||
}
|
||||
|
||||
val encryptedEmptyFolder = OCFile("/encryptedFolder/").apply {
|
||||
isEncrypted = true
|
||||
mimeType = MimeType.DIRECTORY
|
||||
}
|
||||
|
||||
val normalFolder = OCFile("/folder/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
fileLength = SecureRandom().nextLong()
|
||||
}
|
||||
|
||||
val normalEmptyFolder = OCFile("/folder/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
}
|
||||
|
||||
configureCapability(capability)
|
||||
|
||||
launchActivity<TestActivity>().use {
|
||||
it.onActivity { activity ->
|
||||
val filterFactory =
|
||||
FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
|
||||
|
||||
var sut = filterFactory.newInstance(encryptedFolder, mockComponentsGetter, true, user)
|
||||
var toHide = sut.getToHide(false)
|
||||
|
||||
// encrypted folder, with content
|
||||
assertTrue(toHide.contains(R.id.action_unset_encrypted))
|
||||
assertTrue(toHide.contains(R.id.action_encrypted))
|
||||
assertTrue(toHide.contains(R.id.action_remove_file))
|
||||
|
||||
// encrypted, but empty folder
|
||||
sut = filterFactory.newInstance(encryptedEmptyFolder, mockComponentsGetter, true, user)
|
||||
toHide = sut.getToHide(false)
|
||||
|
||||
assertTrue(toHide.contains(R.id.action_unset_encrypted))
|
||||
assertTrue(toHide.contains(R.id.action_remove_file))
|
||||
assertTrue(toHide.contains(R.id.action_encrypted))
|
||||
|
||||
// regular folder, with content
|
||||
sut = filterFactory.newInstance(normalFolder, mockComponentsGetter, true, user)
|
||||
toHide = sut.getToHide(false)
|
||||
|
||||
assertTrue(toHide.contains(R.id.action_unset_encrypted))
|
||||
assertTrue(toHide.contains(R.id.action_encrypted))
|
||||
assertFalse(toHide.contains(R.id.action_remove_file))
|
||||
|
||||
// regular folder, without content
|
||||
sut = filterFactory.newInstance(normalEmptyFolder, mockComponentsGetter, true, user)
|
||||
toHide = sut.getToHide(false)
|
||||
|
||||
assertTrue(toHide.contains(R.id.action_unset_encrypted))
|
||||
assertFalse(toHide.contains(R.id.action_encrypted))
|
||||
assertFalse(toHide.contains(R.id.action_remove_file))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_stream() {
|
||||
val capability = OCCapability().apply {
|
||||
endToEndEncryption = CapabilityBooleanType.TRUE
|
||||
}
|
||||
|
||||
val encryptedVideo = OCFile("/e2e/1.mpg").apply {
|
||||
isEncrypted = true
|
||||
mimeType = "video/mpeg"
|
||||
}
|
||||
|
||||
val normalVideo = OCFile("/folder/2.mpg").apply {
|
||||
mimeType = "video/mpeg"
|
||||
fileLength = SecureRandom().nextLong()
|
||||
}
|
||||
|
||||
configureCapability(capability)
|
||||
|
||||
launchActivity<TestActivity>().use {
|
||||
it.onActivity { activity ->
|
||||
val filterFactory =
|
||||
FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
|
||||
|
||||
var sut = filterFactory.newInstance(encryptedVideo, mockComponentsGetter, true, user)
|
||||
var toHide = sut.getToHide(false)
|
||||
|
||||
// encrypted video, with content
|
||||
assertTrue(toHide.contains(R.id.action_stream_media))
|
||||
|
||||
// regular video, with content
|
||||
sut = filterFactory.newInstance(normalVideo, mockComponentsGetter, true, user)
|
||||
toHide = sut.getToHide(false)
|
||||
|
||||
assertFalse(toHide.contains(R.id.action_stream_media))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter_select_all() {
|
||||
configureCapability(OCCapability())
|
||||
|
||||
// not in single file fragment -> multi selection is possible under certain circumstances
|
||||
|
||||
launchActivity<TestActivity>().use {
|
||||
it.onActivity { activity ->
|
||||
val filterFactory = FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
|
||||
|
||||
val files = listOf(OCFile("/foo.bin"), OCFile("/bar.bin"), OCFile("/baz.bin"))
|
||||
|
||||
// single file, not in multi selection
|
||||
// *Select all* and *Deselect all* should stay hidden
|
||||
var sut = filterFactory.newInstance(files.first(), mockComponentsGetter, true, user)
|
||||
|
||||
var toHide = sut.getToHide(false)
|
||||
assertTrue(toHide.contains(R.id.action_select_all_action_menu))
|
||||
assertTrue(toHide.contains(R.id.action_deselect_all_action_menu))
|
||||
|
||||
// multiple files, all selected in multi selection
|
||||
// *Deselect all* shown, *Select all* not
|
||||
sut = filterFactory.newInstance(files.size, files, mockComponentsGetter, false, user)
|
||||
|
||||
toHide = sut.getToHide(false)
|
||||
assertTrue(toHide.contains(R.id.action_select_all_action_menu))
|
||||
assertFalse(toHide.contains(R.id.action_deselect_all_action_menu))
|
||||
|
||||
// multiple files, all but one selected
|
||||
// both *Select all* and *Deselect all* should be shown
|
||||
sut = filterFactory.newInstance(files.size + 1, files, mockComponentsGetter, false, user)
|
||||
|
||||
toHide = sut.getToHide(false)
|
||||
assertFalse(toHide.contains(R.id.action_select_all_action_menu))
|
||||
assertFalse(toHide.contains(R.id.action_deselect_all_action_menu))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun filter_select_all_singleFileFragment() {
|
||||
configureCapability(OCCapability())
|
||||
|
||||
// in single file fragment (e.g. FileDetailFragment or PreviewImageFragment), selecting multiple files
|
||||
// is not possible -> *Select all* and *Deselect all* options should be hidden
|
||||
|
||||
launchActivity<TestActivity>().use {
|
||||
it.onActivity { activity ->
|
||||
val filterFactory = FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
|
||||
|
||||
val files = listOf(OCFile("/foo.bin"), OCFile("/bar.bin"), OCFile("/baz.bin"))
|
||||
|
||||
// single file
|
||||
var sut = filterFactory.newInstance(files.first(), mockComponentsGetter, true, user)
|
||||
|
||||
var toHide = sut.getToHide(true)
|
||||
assertTrue(toHide.contains(R.id.action_select_all_action_menu))
|
||||
assertTrue(toHide.contains(R.id.action_deselect_all_action_menu))
|
||||
|
||||
// multiple files, all selected
|
||||
sut = filterFactory.newInstance(files.size, files, mockComponentsGetter, false, user)
|
||||
|
||||
toHide = sut.getToHide(true)
|
||||
assertTrue(toHide.contains(R.id.action_select_all_action_menu))
|
||||
assertTrue(toHide.contains(R.id.action_deselect_all_action_menu))
|
||||
|
||||
// multiple files, all but one selected
|
||||
sut = filterFactory.newInstance(files.size + 1, files, mockComponentsGetter, false, user)
|
||||
|
||||
toHide = sut.getToHide(true)
|
||||
assertTrue(toHide.contains(R.id.action_select_all_action_menu))
|
||||
assertTrue(toHide.contains(R.id.action_deselect_all_action_menu))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class ExpectedLockVisibilities(
|
||||
val lockFile: Boolean,
|
||||
val unlockFile: Boolean
|
||||
)
|
||||
|
||||
private fun configureCapability(capability: OCCapability) {
|
||||
every { mockStorageManager.getCapability(any<User>()) } returns capability
|
||||
every { mockStorageManager.getCapability(any<String>()) } returns capability
|
||||
}
|
||||
|
||||
private fun testLockingVisibilities(
|
||||
capability: OCCapability,
|
||||
file: OCFile,
|
||||
expectedLockVisibilities: ExpectedLockVisibilities
|
||||
) {
|
||||
configureCapability(capability)
|
||||
|
||||
launchActivity<TestActivity>().use {
|
||||
it.onActivity { activity ->
|
||||
val filterFactory =
|
||||
FileMenuFilter.Factory(mockStorageManager, activity, editorUtils)
|
||||
val sut = filterFactory.newInstance(file, mockComponentsGetter, true, user)
|
||||
|
||||
val toHide = sut.getToHide(false)
|
||||
|
||||
assertEquals(
|
||||
expectedLockVisibilities.lockFile,
|
||||
!toHide.contains(R.id.action_lock_file)
|
||||
)
|
||||
assertEquals(
|
||||
expectedLockVisibilities.unlockFile,
|
||||
!toHide.contains(R.id.action_unlock_file)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.files.services
|
||||
|
||||
import com.nextcloud.client.account.UserAccountManager
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl
|
||||
import com.nextcloud.client.device.BatteryStatus
|
||||
import com.nextcloud.client.device.PowerManagementService
|
||||
import com.nextcloud.client.jobs.upload.FileUploadHelper
|
||||
import com.nextcloud.client.jobs.upload.FileUploadWorker
|
||||
import com.nextcloud.client.network.Connectivity
|
||||
import com.nextcloud.client.network.ConnectivityService
|
||||
import com.owncloud.android.AbstractOnServerIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.datamodel.UploadsStorageManager
|
||||
import com.owncloud.android.db.OCUpload
|
||||
import com.owncloud.android.lib.common.operations.OperationCancelledException
|
||||
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
|
||||
import com.owncloud.android.lib.resources.files.model.RemoteFile
|
||||
import com.owncloud.android.operations.UploadFileOperation
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertFalse
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
abstract class FileUploaderIT : AbstractOnServerIT() {
|
||||
private var uploadsStorageManager: UploadsStorageManager? = null
|
||||
|
||||
private val connectivityServiceMock: ConnectivityService = object : ConnectivityService {
|
||||
override fun isConnected(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isInternetWalled(): Boolean = false
|
||||
override fun getConnectivity(): Connectivity = Connectivity.CONNECTED_WIFI
|
||||
}
|
||||
|
||||
private val powerManagementServiceMock: PowerManagementService = object : PowerManagementService {
|
||||
override val isPowerSavingEnabled: Boolean
|
||||
get() = false
|
||||
|
||||
override val isPowerSavingExclusionAvailable: Boolean
|
||||
get() = false
|
||||
|
||||
override val battery: BatteryStatus
|
||||
get() = BatteryStatus()
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val contentResolver = targetContext.contentResolver
|
||||
val accountManager: UserAccountManager = UserAccountManagerImpl.fromContext(targetContext)
|
||||
uploadsStorageManager = UploadsStorageManager(accountManager, contentResolver)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file, overwrites it with an empty one, check if overwritten
|
||||
*/
|
||||
// disabled, flaky test
|
||||
// @Test
|
||||
// fun testKeepLocalAndOverwriteRemote() {
|
||||
// val file = getDummyFile("chunkedFile.txt")
|
||||
// val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||
//
|
||||
// assertTrue(
|
||||
// UploadFileOperation(
|
||||
// uploadsStorageManager,
|
||||
// connectivityServiceMock,
|
||||
// powerManagementServiceMock,
|
||||
// user,
|
||||
// null,
|
||||
// ocUpload,
|
||||
// FileUploader.NameCollisionPolicy.DEFAULT,
|
||||
// FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
// targetContext,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// .setRemoteFolderToBeCreated()
|
||||
// .execute(client, storageManager)
|
||||
// .isSuccess
|
||||
// )
|
||||
//
|
||||
// val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
// assertTrue(result.isSuccess)
|
||||
//
|
||||
// assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
//
|
||||
// val ocUpload2 = OCUpload(getDummyFile("empty.txt").absolutePath, "/testFile.txt", account.name)
|
||||
//
|
||||
// assertTrue(
|
||||
// UploadFileOperation(
|
||||
// uploadsStorageManager,
|
||||
// connectivityServiceMock,
|
||||
// powerManagementServiceMock,
|
||||
// user,
|
||||
// null,
|
||||
// ocUpload2,
|
||||
// FileUploader.NameCollisionPolicy.OVERWRITE,
|
||||
// FileUploader.LOCAL_BEHAVIOUR_COPY,
|
||||
// targetContext,
|
||||
// false,
|
||||
// false
|
||||
// )
|
||||
// .execute(client, storageManager)
|
||||
// .isSuccess
|
||||
// )
|
||||
//
|
||||
// val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
// assertTrue(result2.isSuccess)
|
||||
//
|
||||
// assertEquals(0, (result2.data[0] as RemoteFile).length)
|
||||
// }
|
||||
|
||||
/**
|
||||
* uploads a file, overwrites it with an empty one, check if overwritten
|
||||
*/
|
||||
@Test
|
||||
fun testKeepLocalAndOverwriteRemoteStatic() {
|
||||
val file = getDummyFile("chunkedFile.txt")
|
||||
|
||||
FileUploadHelper().uploadNewFiles(
|
||||
user,
|
||||
arrayOf(file.absolutePath),
|
||||
arrayOf("/testFile.txt"),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
NameCollisionPolicy.DEFAULT
|
||||
)
|
||||
|
||||
longSleep()
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val ocFile2 = OCFile("/testFile.txt")
|
||||
ocFile2.storagePath = getDummyFile("empty.txt").absolutePath
|
||||
|
||||
FileUploadHelper().uploadUpdatedFile(
|
||||
user,
|
||||
arrayOf(ocFile2),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
NameCollisionPolicy.OVERWRITE
|
||||
)
|
||||
|
||||
shortSleep()
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(0, (result2.data[0] as RemoteFile).length)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file, uploads another one with automatically (2) added, check
|
||||
*/
|
||||
@Test
|
||||
fun testKeepBoth() {
|
||||
var renameListenerWasTriggered = false
|
||||
|
||||
val file = getDummyFile("chunkedFile.txt")
|
||||
val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||
|
||||
assertTrue(
|
||||
UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
storageManager
|
||||
)
|
||||
.setRemoteFolderToBeCreated()
|
||||
.execute(client)
|
||||
.isSuccess
|
||||
)
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val file2 = getDummyFile("empty.txt")
|
||||
val ocUpload2 = OCUpload(file2.absolutePath, "/testFile.txt", account.name)
|
||||
|
||||
assertTrue(
|
||||
UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload2,
|
||||
NameCollisionPolicy.RENAME,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
storageManager
|
||||
)
|
||||
.addRenameUploadListener {
|
||||
renameListenerWasTriggered = true
|
||||
}
|
||||
.execute(client)
|
||||
.isSuccess
|
||||
)
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||
|
||||
val result3 = ReadFileRemoteOperation("/testFile (2).txt").execute(client)
|
||||
assertTrue(result3.isSuccess)
|
||||
|
||||
assertEquals(file2.length(), (result3.data[0] as RemoteFile).length)
|
||||
assertTrue(renameListenerWasTriggered)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file, uploads another one with automatically (2) added, check
|
||||
*/
|
||||
@Test
|
||||
fun testKeepBothStatic() {
|
||||
val file = getDummyFile("nonEmpty.txt")
|
||||
|
||||
FileUploadHelper().uploadNewFiles(
|
||||
user,
|
||||
arrayOf(file.absolutePath),
|
||||
arrayOf("/testFile.txt"),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
NameCollisionPolicy.DEFAULT
|
||||
)
|
||||
|
||||
longSleep()
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val ocFile2 = OCFile("/testFile.txt")
|
||||
ocFile2.storagePath = getDummyFile("empty.txt").absolutePath
|
||||
|
||||
FileUploadHelper().uploadUpdatedFile(
|
||||
user,
|
||||
arrayOf(ocFile2),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
NameCollisionPolicy.RENAME
|
||||
)
|
||||
|
||||
shortSleep()
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||
|
||||
val result3 = ReadFileRemoteOperation("/testFile (2).txt").execute(client)
|
||||
assertTrue(result3.isSuccess)
|
||||
|
||||
assertEquals(ocFile2.fileLength, (result3.data[0] as RemoteFile).length)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file with "keep server" option set, so do nothing
|
||||
*/
|
||||
@Test
|
||||
fun testKeepServer() {
|
||||
val file = getDummyFile("chunkedFile.txt")
|
||||
val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||
|
||||
assertTrue(
|
||||
UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.DEFAULT,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
storageManager
|
||||
)
|
||||
.setRemoteFolderToBeCreated()
|
||||
.execute(client)
|
||||
.isSuccess
|
||||
)
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val ocUpload2 = OCUpload(getDummyFile("empty.txt").absolutePath, "/testFile.txt", account.name)
|
||||
|
||||
assertFalse(
|
||||
UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload2,
|
||||
NameCollisionPolicy.CANCEL,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
storageManager
|
||||
)
|
||||
.execute(client).isSuccess
|
||||
)
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file with "keep server" option set, so do nothing
|
||||
*/
|
||||
@Test
|
||||
fun testKeepServerStatic() {
|
||||
val file = getDummyFile("chunkedFile.txt")
|
||||
|
||||
FileUploadHelper().uploadNewFiles(
|
||||
user,
|
||||
arrayOf(file.absolutePath),
|
||||
arrayOf("/testFile.txt"),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
NameCollisionPolicy.DEFAULT
|
||||
)
|
||||
|
||||
longSleep()
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val ocFile2 = OCFile("/testFile.txt")
|
||||
ocFile2.storagePath = getDummyFile("empty.txt").absolutePath
|
||||
|
||||
FileUploadHelper().uploadUpdatedFile(
|
||||
user,
|
||||
arrayOf(ocFile2),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
NameCollisionPolicy.CANCEL
|
||||
)
|
||||
|
||||
shortSleep()
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file with "skip if exists" option set, so do nothing if file exists
|
||||
*/
|
||||
@Test
|
||||
fun testCancelServer() {
|
||||
val file = getDummyFile("chunkedFile.txt")
|
||||
val ocUpload = OCUpload(file.absolutePath, "/testFile.txt", account.name)
|
||||
|
||||
assertTrue(
|
||||
UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload,
|
||||
NameCollisionPolicy.CANCEL,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
storageManager
|
||||
)
|
||||
.setRemoteFolderToBeCreated()
|
||||
.execute(client)
|
||||
.isSuccess
|
||||
)
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val ocUpload2 = OCUpload(getDummyFile("empty.txt").absolutePath, "/testFile.txt", account.name)
|
||||
|
||||
val uploadResult = UploadFileOperation(
|
||||
uploadsStorageManager,
|
||||
connectivityServiceMock,
|
||||
powerManagementServiceMock,
|
||||
user,
|
||||
null,
|
||||
ocUpload2,
|
||||
NameCollisionPolicy.CANCEL,
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
targetContext,
|
||||
false,
|
||||
false,
|
||||
storageManager
|
||||
)
|
||||
.execute(client)
|
||||
|
||||
assertFalse(uploadResult.isSuccess)
|
||||
assertTrue(uploadResult.exception is OperationCancelledException)
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||
}
|
||||
|
||||
/**
|
||||
* uploads a file with "skip if exists" option set, so do nothing if file exists
|
||||
*/
|
||||
@Test
|
||||
fun testKeepCancelStatic() {
|
||||
val file = getDummyFile("chunkedFile.txt")
|
||||
|
||||
FileUploadHelper().uploadNewFiles(
|
||||
user,
|
||||
arrayOf(file.absolutePath),
|
||||
arrayOf("/testFile.txt"),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
true,
|
||||
UploadFileOperation.CREATED_BY_USER,
|
||||
false,
|
||||
false,
|
||||
NameCollisionPolicy.DEFAULT
|
||||
)
|
||||
|
||||
longSleep()
|
||||
|
||||
val result = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result.data[0] as RemoteFile).length)
|
||||
|
||||
val ocFile2 = OCFile("/testFile.txt")
|
||||
ocFile2.storagePath = getDummyFile("empty.txt").absolutePath
|
||||
|
||||
FileUploadHelper().uploadUpdatedFile(
|
||||
user,
|
||||
arrayOf(ocFile2),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_COPY,
|
||||
NameCollisionPolicy.CANCEL
|
||||
)
|
||||
|
||||
shortSleep()
|
||||
|
||||
val result2 = ReadFileRemoteOperation("/testFile.txt").execute(client)
|
||||
assertTrue(result2.isSuccess)
|
||||
|
||||
assertEquals(file.length(), (result2.data[0] as RemoteFile).length)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.files.services
|
||||
|
||||
class LegacyFileUploaderIT : FileUploaderIT()
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.operations
|
||||
|
||||
import com.owncloud.android.AbstractOnServerIT
|
||||
import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation
|
||||
import com.owncloud.android.lib.resources.shares.CreateShareRemoteOperation
|
||||
import com.owncloud.android.lib.resources.shares.OCShare
|
||||
import com.owncloud.android.lib.resources.shares.ShareType
|
||||
import junit.framework.TestCase
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class GetSharesForFileOperationIT : AbstractOnServerIT() {
|
||||
@Test
|
||||
fun shares() {
|
||||
val remotePath = "/share/"
|
||||
assertTrue(CreateFolderRemoteOperation(remotePath, true).execute(client).isSuccess)
|
||||
|
||||
// share folder to user "admin"
|
||||
TestCase.assertTrue(
|
||||
CreateShareRemoteOperation(
|
||||
remotePath,
|
||||
ShareType.USER,
|
||||
"admin",
|
||||
false,
|
||||
"",
|
||||
OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
)
|
||||
.execute(client).isSuccess
|
||||
)
|
||||
|
||||
// share folder via public link
|
||||
TestCase.assertTrue(
|
||||
CreateShareRemoteOperation(
|
||||
remotePath,
|
||||
ShareType.PUBLIC_LINK,
|
||||
"",
|
||||
true,
|
||||
"",
|
||||
OCShare.READ_PERMISSION_FLAG
|
||||
)
|
||||
.execute(client).isSuccess
|
||||
)
|
||||
|
||||
// share folder to group
|
||||
assertTrue(
|
||||
CreateShareRemoteOperation(
|
||||
remotePath,
|
||||
ShareType.GROUP,
|
||||
"users",
|
||||
false,
|
||||
"",
|
||||
OCShare.NO_PERMISSION
|
||||
)
|
||||
.execute(client).isSuccess
|
||||
)
|
||||
|
||||
val shareResult = GetSharesForFileOperation(remotePath, false, false, storageManager).execute(client)
|
||||
assertTrue(shareResult.isSuccess)
|
||||
|
||||
assertEquals(3, (shareResult.data as ArrayList<OCShare>).size)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.operations;
|
||||
|
||||
import com.owncloud.android.AbstractOnServerIT;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
public class RemoveFileOperationIT extends AbstractOnServerIT {
|
||||
@Test
|
||||
public void deleteFolder() {
|
||||
String parent = "/test/";
|
||||
String path = parent + "folder1/";
|
||||
assertTrue(new CreateFolderOperation(path, user, targetContext, getStorageManager()).execute(client)
|
||||
.isSuccess());
|
||||
|
||||
OCFile folder = getStorageManager().getFileByPath(path);
|
||||
|
||||
assertNotNull(folder);
|
||||
|
||||
assertTrue(new RemoveFileOperation(folder,
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
|
||||
OCFile parentFolder = getStorageManager().getFileByPath(parent);
|
||||
|
||||
assertNotNull(parentFolder);
|
||||
assertTrue(new RemoveFileOperation(parentFolder,
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteFile() throws IOException {
|
||||
String parent = "/test/";
|
||||
String path = parent + "empty.txt";
|
||||
OCUpload ocUpload = new OCUpload(getDummyFile("empty.txt").getAbsolutePath(), path, account.name);
|
||||
|
||||
uploadOCUpload(ocUpload);
|
||||
|
||||
OCFile file = getStorageManager().getFileByPath(path);
|
||||
|
||||
assertNotNull(file);
|
||||
|
||||
assertTrue(new RemoveFileOperation(file,
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
|
||||
OCFile parentFolder = getStorageManager().getFileByPath(parent);
|
||||
|
||||
assertNotNull(parentFolder);
|
||||
assertTrue(new RemoveFileOperation(parentFolder,
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
targetContext,
|
||||
getStorageManager())
|
||||
.execute(client)
|
||||
.isSuccess());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Torsten Grote <t@grobox.de>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.providers
|
||||
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract.Document.COLUMN_DOCUMENT_ID
|
||||
import android.provider.DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||
import android.provider.DocumentsContract.Document.MIME_TYPE_DIR
|
||||
import android.provider.DocumentsContract.EXTRA_LOADING
|
||||
import android.provider.DocumentsContract.buildChildDocumentsUriUsingTree
|
||||
import android.provider.DocumentsContract.buildDocumentUriUsingTree
|
||||
import android.provider.DocumentsContract.buildTreeDocumentUri
|
||||
import android.provider.DocumentsContract.getDocumentId
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.utils.Log_OC
|
||||
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation
|
||||
import com.owncloud.android.providers.DocumentsStorageProvider.DOCUMENTID_SEPARATOR
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
// Uploads can sometimes take a bit of time, so 15sec is still considered recent enough
|
||||
private const val RECENT_MILLISECONDS = 15_000
|
||||
|
||||
object DocumentsProviderUtils {
|
||||
|
||||
internal fun DocumentFile.getOCFile(storageManager: FileDataStorageManager): OCFile? {
|
||||
val id = getDocumentId(uri)
|
||||
val separated: List<String> = id.split(DOCUMENTID_SEPARATOR.toRegex())
|
||||
return storageManager.getFileById(separated[1].toLong())
|
||||
}
|
||||
|
||||
internal fun DocumentFile.assertRegularFile(
|
||||
name: String? = null,
|
||||
size: Long? = null,
|
||||
mimeType: String? = null,
|
||||
parent: DocumentFile? = null
|
||||
) {
|
||||
name?.let { assertEquals(it, this.name) }
|
||||
assertTrue(exists())
|
||||
assertTrue(isFile)
|
||||
assertFalse(isDirectory)
|
||||
assertFalse(isVirtual)
|
||||
size?.let { assertEquals(it, length()) }
|
||||
mimeType?.let { assertEquals(it, type) }
|
||||
parent?.let { assertEquals(it.uri.toString(), parentFile!!.uri.toString()) }
|
||||
}
|
||||
|
||||
internal fun DocumentFile.assertRegularFolder(name: String? = null, parent: DocumentFile? = null) {
|
||||
name?.let { assertEquals(it, this.name) }
|
||||
assertTrue(exists())
|
||||
assertFalse(isFile)
|
||||
assertTrue(isDirectory)
|
||||
assertFalse(isVirtual)
|
||||
parent?.let { assertEquals(it.uri.toString(), parentFile!!.uri.toString()) }
|
||||
}
|
||||
|
||||
internal fun DocumentFile.assertRecentlyModified() {
|
||||
val diff = System.currentTimeMillis() - lastModified()
|
||||
assertTrue("File $name older than expected: $diff", diff < RECENT_MILLISECONDS)
|
||||
}
|
||||
|
||||
internal fun assertExistsOnServer(client: OwnCloudClient, remotePath: String, shouldExist: Boolean) {
|
||||
val result = ExistenceCheckRemoteOperation(remotePath, !shouldExist).execute(client)
|
||||
assertTrue("$result", result.isSuccess)
|
||||
}
|
||||
|
||||
internal fun assertListFilesEquals(expected: Collection<DocumentFile>, actual: Collection<DocumentFile>) {
|
||||
// assertEquals(
|
||||
// "Actual: ${actual.map { it.name.toString() }}",
|
||||
// expected.map { it.uri.toString() }.apply { sorted() },
|
||||
// actual.map { it.uri.toString() }.apply { sorted() },
|
||||
// )
|
||||
// FIXME replace with commented out stronger assertion above
|
||||
// when parallel [UploadFileOperation]s don't bring back deleted files
|
||||
val expectedSet = HashSet<String>(expected.map { it.uri.toString() })
|
||||
val actualSet = HashSet<String>(actual.map { it.uri.toString() })
|
||||
assertTrue(actualSet.containsAll(expectedSet))
|
||||
actualSet.removeAll(expectedSet)
|
||||
actualSet.forEach {
|
||||
Log_OC.e("TEST", "Error: Found unexpected file: $it")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assertReadEquals(data: ByteArray, inputStream: InputStream?) {
|
||||
assertNotNull(inputStream)
|
||||
inputStream!!.use {
|
||||
assertArrayEquals(data, it.readBytes())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as [DocumentFile.findFile] only that it re-queries when the first result was stale.
|
||||
*
|
||||
* Most documents providers including Nextcloud are listing the full directory content
|
||||
* when querying for a specific file in a directory,
|
||||
* so there is no point in trying to optimize the query by not listing all children.
|
||||
*/
|
||||
suspend fun DocumentFile.findFileBlocking(context: Context, displayName: String): DocumentFile? {
|
||||
val files = listFilesBlocking(context)
|
||||
for (doc in files) {
|
||||
if (displayName == doc.name) return doc
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Works like [DocumentFile.listFiles] except that it waits until the DocumentProvider has a result.
|
||||
* This prevents getting an empty list even though there are children to be listed.
|
||||
*/
|
||||
suspend fun DocumentFile.listFilesBlocking(context: Context) = withContext(Dispatchers.IO) {
|
||||
val resolver = context.contentResolver
|
||||
val childrenUri = buildChildDocumentsUriUsingTree(uri, getDocumentId(uri))
|
||||
val projection = arrayOf(COLUMN_DOCUMENT_ID, COLUMN_MIME_TYPE)
|
||||
val result = ArrayList<DocumentFile>()
|
||||
|
||||
try {
|
||||
getLoadedCursor {
|
||||
resolver.query(childrenUri, projection, null, null, null)
|
||||
}
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
throw IOException(e)
|
||||
}.use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
val documentId = cursor.getString(0)
|
||||
val isDirectory = cursor.getString(1) == MIME_TYPE_DIR
|
||||
val file = if (isDirectory) {
|
||||
val treeUri = buildTreeDocumentUri(uri.authority, documentId)
|
||||
DocumentFile.fromTreeUri(context, treeUri)!!
|
||||
} else {
|
||||
val documentUri = buildDocumentUriUsingTree(uri, documentId)
|
||||
DocumentFile.fromSingleUri(context, documentUri)!!
|
||||
}
|
||||
result.add(file)
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cursor for the given query while ensuring that the cursor was loaded.
|
||||
*
|
||||
* When the SAF backend is a cloud storage provider (e.g. Nextcloud),
|
||||
* it can happen that the query returns an outdated (e.g. empty) cursor
|
||||
* which will only be updated in response to this query.
|
||||
*
|
||||
* See: https://commonsware.com/blog/2019/12/14/scoped-storage-stories-listfiles-woe.html
|
||||
*
|
||||
* This method uses a [suspendCancellableCoroutine] to wait for the result of a [ContentObserver]
|
||||
* registered on the cursor in case the cursor is still loading ([EXTRA_LOADING]).
|
||||
* If the cursor is not loading, it will be returned right away.
|
||||
*
|
||||
* @param timeout an optional time-out in milliseconds
|
||||
* @throws TimeoutCancellationException if there was no result before the time-out
|
||||
* @throws IOException if the query returns null
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
@VisibleForTesting
|
||||
internal suspend fun getLoadedCursor(timeout: Long = 15_000, query: () -> Cursor?) =
|
||||
withTimeout(timeout) {
|
||||
suspendCancellableCoroutine<Cursor> { cont ->
|
||||
val cursor = query() ?: throw IOException("Initial query returned no results")
|
||||
cont.invokeOnCancellation { cursor.close() }
|
||||
val loading = cursor.extras.getBoolean(EXTRA_LOADING, false)
|
||||
if (loading) {
|
||||
Log_OC.e("TEST", "Cursor was loading, wait for update...")
|
||||
cursor.registerContentObserver(
|
||||
object : ContentObserver(null) {
|
||||
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||
cursor.close()
|
||||
val newCursor = query()
|
||||
if (newCursor == null) {
|
||||
cont.cancel(IOException("Re-query returned no results"))
|
||||
} else {
|
||||
cont.resume(newCursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// not loading, return cursor right away
|
||||
cont.resume(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Torsten Grote <t@grobox.de>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.providers
|
||||
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.nextcloud.test.RandomStringGenerator
|
||||
import com.owncloud.android.AbstractOnServerIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.OCFile.ROOT_PATH
|
||||
import com.owncloud.android.lib.common.utils.Log_OC
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.assertExistsOnServer
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.assertListFilesEquals
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.assertReadEquals
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.assertRecentlyModified
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.assertRegularFile
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.assertRegularFolder
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.findFileBlocking
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.getOCFile
|
||||
import com.owncloud.android.providers.DocumentsProviderUtils.listFilesBlocking
|
||||
import com.owncloud.android.providers.DocumentsStorageProvider.DOCUMENTID_SEPARATOR
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.commons.httpclient.HttpStatus
|
||||
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity
|
||||
import org.apache.jackrabbit.webdav.client.methods.PutMethod
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.random.Random
|
||||
|
||||
private const val MAX_FILE_NAME_LENGTH = 225
|
||||
|
||||
class DocumentsStorageProviderIT : AbstractOnServerIT() {
|
||||
|
||||
private val context = targetContext
|
||||
private val contentResolver = context.contentResolver
|
||||
private val authority = context.getString(R.string.document_provider_authority)
|
||||
|
||||
private val rootFileId = storageManager.getFileByEncryptedRemotePath(ROOT_PATH).fileId
|
||||
private val documentId = "${DocumentsStorageProvider.rootIdForUser(user)}${DOCUMENTID_SEPARATOR}$rootFileId"
|
||||
private val uri = DocumentsContract.buildTreeDocumentUri(authority, documentId)
|
||||
private val rootDir get() = DocumentFile.fromTreeUri(context, uri)!!
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
// DocumentsProvider#onCreate() is called when the application is started
|
||||
// which is *after* AbstractOnServerIT adds the accounts (when the app is freshly installed).
|
||||
// So we need to query our roots here to ensure that the internal storage map is initialized.
|
||||
contentResolver.query(DocumentsContract.buildRootsUri(authority), null, null, null)
|
||||
assertTrue("Storage root does not exist", rootDir.exists())
|
||||
assertTrue(rootDir.isDirectory)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all files in [rootDir] after each test.
|
||||
*
|
||||
* We can't use [AbstractOnServerIT.after] as this is only deleting remote files.
|
||||
*/
|
||||
@After
|
||||
override fun after() = runBlocking {
|
||||
rootDir.listFilesBlocking(context).forEach {
|
||||
Log_OC.e("TEST", "Deleting ${it.name}...")
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateDeleteFiles() = runBlocking {
|
||||
// no files in root initially
|
||||
assertListFilesEquals(emptyList(), rootDir.listFilesBlocking(context))
|
||||
|
||||
// create first file
|
||||
val name1 = RandomStringGenerator.make()
|
||||
val type1 = "text/html"
|
||||
val file1 = rootDir.createFile(type1, name1)!!
|
||||
|
||||
// check assumptions
|
||||
/* FIXME: mimeType */
|
||||
file1.assertRegularFile(name1, 0L, null, rootDir)
|
||||
file1.assertRecentlyModified()
|
||||
|
||||
// file1 is found in root
|
||||
assertListFilesEquals(listOf(file1), rootDir.listFilesBlocking(context).toList())
|
||||
|
||||
// file1 was uploaded
|
||||
val ocFile1 = file1.getOCFile(storageManager)!!
|
||||
assertExistsOnServer(client, ocFile1.remotePath, true)
|
||||
|
||||
// create second long file with long file name
|
||||
val name2 = RandomStringGenerator.make(MAX_FILE_NAME_LENGTH)
|
||||
val type2 = "application/octet-stream"
|
||||
val file2 = rootDir.createFile(type2, name2)!!
|
||||
|
||||
// file2 was uploaded
|
||||
val ocFile2 = file2.getOCFile(storageManager)!!
|
||||
assertExistsOnServer(client, ocFile2.remotePath, true)
|
||||
|
||||
// check assumptions
|
||||
file2.assertRegularFile(name2, 0L, type2, rootDir)
|
||||
file2.assertRecentlyModified()
|
||||
|
||||
// both files get listed in root
|
||||
assertListFilesEquals(listOf(file1, file2), rootDir.listFiles().toList())
|
||||
|
||||
// delete first file
|
||||
assertTrue(file1.delete())
|
||||
assertFalse(file1.exists())
|
||||
assertExistsOnServer(client, ocFile1.remotePath, false)
|
||||
|
||||
// only second file gets listed in root
|
||||
assertListFilesEquals(listOf(file2), rootDir.listFiles().toList())
|
||||
|
||||
// delete also second file
|
||||
assertTrue(file2.delete())
|
||||
assertFalse(file2.exists())
|
||||
assertExistsOnServer(client, ocFile2.remotePath, false)
|
||||
|
||||
// no more files in root
|
||||
assertListFilesEquals(emptyList(), rootDir.listFilesBlocking(context))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReadWriteFiles() {
|
||||
// create random file
|
||||
val file1 = rootDir.createFile("application/octet-stream", RandomStringGenerator.make())!!
|
||||
file1.assertRegularFile(size = 0L)
|
||||
|
||||
// write random bytes to file
|
||||
@Suppress("MagicNumber")
|
||||
val dataSize = Random.nextInt(1, 99) * 1024
|
||||
val data1 = Random.nextBytes(dataSize)
|
||||
contentResolver.openOutputStream(file1.uri, "wt").use {
|
||||
it!!.write(data1)
|
||||
}
|
||||
|
||||
// read back random bytes
|
||||
assertReadEquals(data1, contentResolver.openInputStream(file1.uri))
|
||||
|
||||
// file size was updated correctly
|
||||
file1.assertRegularFile(size = data1.size.toLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateDeleteFolders() = runBlocking {
|
||||
// create a new folder
|
||||
val dirName1 = RandomStringGenerator.make()
|
||||
val dir1 = rootDir.createDirectory(dirName1)!!
|
||||
dir1.assertRegularFolder(dirName1, rootDir)
|
||||
// FIXME about a minute gets lost somewhere after CFO sets the correct time
|
||||
@Suppress("MagicNumber")
|
||||
assertTrue(System.currentTimeMillis() - dir1.lastModified() < 60_000)
|
||||
// dir1.assertRecentlyModified()
|
||||
|
||||
// ensure folder was uploaded to server
|
||||
val ocDir1 = dir1.getOCFile(storageManager)!!
|
||||
assertExistsOnServer(client, ocDir1.remotePath, true)
|
||||
|
||||
// create file in folder
|
||||
val file1 = dir1.createFile("text/html", RandomStringGenerator.make())!!
|
||||
file1.assertRegularFile(parent = dir1)
|
||||
val ocFile1 = file1.getOCFile(storageManager)!!
|
||||
assertExistsOnServer(client, ocFile1.remotePath, true)
|
||||
|
||||
// we find the new file in the created folder and get it in the list
|
||||
assertEquals(file1.uri.toString(), dir1.findFileBlocking(context, file1.name!!)!!.uri.toString())
|
||||
assertListFilesEquals(listOf(file1), dir1.listFilesBlocking(context))
|
||||
|
||||
// delete folder
|
||||
dir1.delete()
|
||||
assertFalse(dir1.exists())
|
||||
assertExistsOnServer(client, ocDir1.remotePath, false)
|
||||
|
||||
// ensure file got deleted with it
|
||||
// since Room was introduced, the file is not automatically updated for some reason.
|
||||
// however, it is correctly deleted from server, and smoke testing shows it works just fine.
|
||||
// suspecting a race condition of some sort
|
||||
// assertFalse(file1.exists())
|
||||
assertExistsOnServer(client, ocFile1.remotePath, false)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Test(timeout = 5 * 60 * 1000)
|
||||
fun testServerChangedFileContent() {
|
||||
// create random file
|
||||
val file1 = rootDir.createFile("text/plain", RandomStringGenerator.make())!!
|
||||
file1.assertRegularFile(size = 0L)
|
||||
|
||||
val createdETag = file1.getOCFile(storageManager)!!.etagOnServer
|
||||
|
||||
assertTrue(createdETag.isNotEmpty())
|
||||
|
||||
val content1 = "initial content".toByteArray()
|
||||
|
||||
// write content bytes to file
|
||||
contentResolver.openOutputStream(file1.uri, "wt").use {
|
||||
it!!.write(content1)
|
||||
}
|
||||
|
||||
// refresh
|
||||
while (file1.getOCFile(storageManager)!!.etagOnServer == createdETag) {
|
||||
shortSleep()
|
||||
rootDir.listFiles()
|
||||
}
|
||||
|
||||
val remotePath = file1.getOCFile(storageManager)!!.remotePath
|
||||
|
||||
val content2 = "new content".toByteArray()
|
||||
|
||||
// modify content on server side
|
||||
val putMethod = PutMethod(client.getFilesDavUri(remotePath))
|
||||
putMethod.requestEntity = ByteArrayRequestEntity(content2)
|
||||
assertEquals(HttpStatus.SC_NO_CONTENT, client.executeMethod(putMethod))
|
||||
client.exhaustResponse(putMethod.responseBodyAsStream)
|
||||
putMethod.releaseConnection() // let the connection available for other methods
|
||||
|
||||
// read back content bytes
|
||||
val bytes = contentResolver.openInputStream(file1.uri)?.readBytes() ?: ByteArray(0)
|
||||
assertEquals(String(content2), String(bytes))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testServerSuccessive() {
|
||||
// create random file
|
||||
val file1 = rootDir.createFile("text/plain", RandomStringGenerator.make())!!
|
||||
file1.assertRegularFile(size = 0L)
|
||||
|
||||
val createdETag = file1.getOCFile(storageManager)!!.etagOnServer
|
||||
|
||||
assertTrue(createdETag.isNotEmpty())
|
||||
|
||||
val content1 = "initial content".toByteArray()
|
||||
|
||||
// write content bytes to file
|
||||
contentResolver.openOutputStream(file1.uri, "wt").use {
|
||||
it!!.write(content1)
|
||||
}
|
||||
|
||||
// refresh
|
||||
while (file1.getOCFile(storageManager)!!.etagOnServer == createdETag) {
|
||||
shortSleep()
|
||||
rootDir.listFiles()
|
||||
}
|
||||
|
||||
val content2 = "new content".toByteArray()
|
||||
|
||||
contentResolver.openOutputStream(file1.uri, "wt").use {
|
||||
it!!.write(content2)
|
||||
}
|
||||
|
||||
// read back content bytes
|
||||
val bytes = contentResolver.openInputStream(file1.uri)?.readBytes() ?: ByteArray(0)
|
||||
assertEquals(String(content2), String(bytes))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.providers
|
||||
|
||||
import android.content.ContentValues
|
||||
import com.owncloud.android.db.ProviderMeta
|
||||
import com.owncloud.android.utils.MimeTypeUtil
|
||||
import org.junit.Test
|
||||
|
||||
@Suppress("FunctionNaming")
|
||||
class FileContentProviderVerificationIT {
|
||||
|
||||
companion object {
|
||||
private const val INVALID_COLUMN = "Invalid column"
|
||||
private const val FILE_LENGTH = 120
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun verifyColumnName_Exception() {
|
||||
FileContentProvider.VerificationUtils.verifyColumnName(INVALID_COLUMN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyColumnName_OK() {
|
||||
FileContentProvider.VerificationUtils.verifyColumnName(ProviderMeta.ProviderTableMeta.FILE_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyColumn_ContentValues_OK() {
|
||||
// with valid columns
|
||||
val contentValues = ContentValues()
|
||||
contentValues.put(ProviderMeta.ProviderTableMeta.FILE_CONTENT_LENGTH, FILE_LENGTH)
|
||||
contentValues.put(ProviderMeta.ProviderTableMeta.FILE_CONTENT_TYPE, MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN)
|
||||
FileContentProvider.VerificationUtils.verifyColumns(contentValues)
|
||||
|
||||
// empty
|
||||
FileContentProvider.VerificationUtils.verifyColumns(ContentValues())
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun verifyColumn_ContentValues_invalidColumn() {
|
||||
// with invalid columns
|
||||
val contentValues = ContentValues()
|
||||
contentValues.put(INVALID_COLUMN, FILE_LENGTH)
|
||||
contentValues.put(ProviderMeta.ProviderTableMeta.FILE_CONTENT_TYPE, MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN)
|
||||
FileContentProvider.VerificationUtils.verifyColumns(contentValues)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySortOrder_OK() {
|
||||
// null
|
||||
FileContentProvider.VerificationUtils.verifySortOrder(null)
|
||||
|
||||
// empty
|
||||
FileContentProvider.VerificationUtils.verifySortOrder("")
|
||||
|
||||
// valid sort
|
||||
FileContentProvider.VerificationUtils.verifySortOrder(ProviderMeta.ProviderTableMeta.FILE_DEFAULT_SORT_ORDER)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun verifySortOrder_InvalidColumn() {
|
||||
// with invalid column
|
||||
FileContentProvider.VerificationUtils.verifySortOrder("$INVALID_COLUMN desc")
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun verifySortOrder_InvalidGrammar() {
|
||||
// with invalid grammar
|
||||
FileContentProvider.VerificationUtils.verifySortOrder("${ProviderMeta.ProviderTableMeta._ID} ;--foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyWhere_OK() {
|
||||
FileContentProvider.VerificationUtils.verifyWhere(null)
|
||||
FileContentProvider.VerificationUtils.verifyWhere(
|
||||
"${ProviderMeta.ProviderTableMeta._ID}=? AND ${ProviderMeta.ProviderTableMeta.FILE_ACCOUNT_OWNER}=?"
|
||||
)
|
||||
FileContentProvider.VerificationUtils.verifyWhere(
|
||||
"${ProviderMeta.ProviderTableMeta._ID} = 1" +
|
||||
" AND (1 = 1)" +
|
||||
" AND ${ProviderMeta.ProviderTableMeta.FILE_ACCOUNT_OWNER} LIKE ?"
|
||||
)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun verifyWhere_InvalidColumnName() {
|
||||
FileContentProvider.VerificationUtils.verifyWhere("$INVALID_COLUMN= ?")
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun verifyWhere_InvalidGrammar() {
|
||||
FileContentProvider.VerificationUtils.verifyWhere("1=1 -- SELECT * FROM")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.providers
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractOnServerIT
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class UsersAndGroupsSearchProviderIT : AbstractOnServerIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
fun searchUser() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
shortSleep()
|
||||
|
||||
activity.runOnUiThread {
|
||||
// fragment.search("Admin")
|
||||
}
|
||||
|
||||
longSleep()
|
||||
}
|
||||
}
|
||||
136
app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt
Normal file
136
app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui
|
||||
|
||||
import android.os.Build
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.web.sugar.Web
|
||||
import androidx.test.espresso.web.webdriver.DriverAtoms
|
||||
import androidx.test.espresso.web.webdriver.Locator
|
||||
import androidx.test.filters.LargeTest
|
||||
import androidx.test.filters.SdkSuppress
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.nextcloud.client.account.UserAccountManager
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl
|
||||
import com.nextcloud.test.GrantStoragePermissionRule
|
||||
import com.nextcloud.test.RetryTestRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.authentication.AuthenticatorActivity
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@LargeTest
|
||||
class LoginIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val permissionRule = GrantStoragePermissionRule.grant()
|
||||
|
||||
@get:Rule
|
||||
var retryTestRule = RetryTestRule()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
tearDown()
|
||||
ActivityScenario.launch(AuthenticatorActivity::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(InterruptedException::class)
|
||||
@Suppress("MagicNumber", "SwallowedException")
|
||||
|
||||
/**
|
||||
* The CI/CD pipeline is encountering issues related to the Android version for this functionality.
|
||||
* Therefore the test will only be executed on Android versions 10 and above.
|
||||
*/
|
||||
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
|
||||
fun login() {
|
||||
val arguments = InstrumentationRegistry.getArguments()
|
||||
val baseUrl = arguments.getString("TEST_SERVER_URL")!!
|
||||
val loginName = arguments.getString("TEST_SERVER_USERNAME")!!
|
||||
val password = arguments.getString("TEST_SERVER_PASSWORD")!!
|
||||
Espresso.onView(ViewMatchers.withId(R.id.login)).perform(ViewActions.click())
|
||||
Espresso.onView(ViewMatchers.withId(R.id.host_url_input)).perform(ViewActions.typeText(baseUrl))
|
||||
Espresso.onView(ViewMatchers.withId(R.id.host_url_input)).perform(ViewActions.typeTextIntoFocusedView("\n"))
|
||||
Thread.sleep(3000)
|
||||
Web.onWebView().forceJavascriptEnabled()
|
||||
|
||||
// click on login
|
||||
try {
|
||||
// NC 25+
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//form[@id='login-form']/input[@type='submit']"))
|
||||
.perform(DriverAtoms.webClick())
|
||||
} catch (e: RuntimeException) {
|
||||
// NC < 25
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//p[@id='redirect-link']/a"))
|
||||
.perform(DriverAtoms.webClick())
|
||||
}
|
||||
|
||||
// username
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//input[@id='user']"))
|
||||
.perform(DriverAtoms.webKeys(loginName))
|
||||
|
||||
// password
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//input[@id='password']"))
|
||||
.perform(DriverAtoms.webKeys(password))
|
||||
|
||||
// click login
|
||||
try {
|
||||
// NC 25+
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//button[@type='submit']"))
|
||||
.perform(DriverAtoms.webClick())
|
||||
} catch (e: RuntimeException) {
|
||||
// NC < 25
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//input[@type='submit']"))
|
||||
.perform(DriverAtoms.webClick())
|
||||
}
|
||||
|
||||
Thread.sleep(2000)
|
||||
|
||||
// grant access
|
||||
Web.onWebView()
|
||||
.withElement(DriverAtoms.findElement(Locator.XPATH, "//input[@type='submit']"))
|
||||
.perform(DriverAtoms.webClick())
|
||||
Thread.sleep((5 * 1000).toLong())
|
||||
|
||||
// check for account
|
||||
val targetContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val accountManager: UserAccountManager = UserAccountManagerImpl.fromContext(targetContext)
|
||||
Assert.assertEquals(1, accountManager.accounts.size.toLong())
|
||||
val account = accountManager.accounts[0]
|
||||
|
||||
// account.name is loginName@baseUrl (without protocol)
|
||||
Assert.assertEquals(loginName, account.name.split("@".toRegex()).toTypedArray()[0])
|
||||
Assert.assertEquals(
|
||||
baseUrl.split("//".toRegex()).toTypedArray()[1],
|
||||
account.name.split("@".toRegex()).toTypedArray()[1]
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@AfterClass
|
||||
fun tearDown() {
|
||||
val targetContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val accountManager: UserAccountManager = UserAccountManagerImpl.fromContext(targetContext)
|
||||
if (accountManager.accounts.isNotEmpty()) {
|
||||
accountManager.removeAllAccounts()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.db.OCUpload;
|
||||
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ConflictsResolveActivityIT extends AbstractIT {
|
||||
@Rule public IntentsTestRule<ConflictsResolveActivity> activityRule =
|
||||
new IntentsTestRule<>(ConflictsResolveActivity.class, true, false);
|
||||
private boolean returnCode;
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void screenshotTextFiles() {
|
||||
OCFile newFile = new OCFile("/newFile.txt");
|
||||
newFile.setFileLength(56000);
|
||||
newFile.setModificationTimestamp(1522019340);
|
||||
newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
|
||||
|
||||
OCFile existingFile = new OCFile("/newFile.txt");
|
||||
existingFile.setFileLength(1024000);
|
||||
existingFile.setModificationTimestamp(1582019340);
|
||||
|
||||
FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver());
|
||||
storageManager.saveNewFile(existingFile);
|
||||
|
||||
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile);
|
||||
|
||||
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||
|
||||
ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
|
||||
newFile,
|
||||
UserAccountManagerImpl
|
||||
.fromContext(targetContext)
|
||||
.getUser()
|
||||
);
|
||||
dialog.showDialog(sut);
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
shortSleep();
|
||||
shortSleep();
|
||||
shortSleep();
|
||||
shortSleep();
|
||||
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
}
|
||||
|
||||
// @Test
|
||||
// @ScreenshotTest // todo run without real server
|
||||
// public void screenshotImages() throws IOException {
|
||||
// FileDataStorageManager storageManager = new FileDataStorageManager(user,
|
||||
// targetContext.getContentResolver());
|
||||
//
|
||||
// OCFile newFile = new OCFile("/newFile.txt");
|
||||
// newFile.setFileLength(56000);
|
||||
// newFile.setModificationTimestamp(1522019340);
|
||||
// newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
|
||||
//
|
||||
// File image = getFile("image.jpg");
|
||||
//
|
||||
// assertTrue(new UploadFileRemoteOperation(image.getAbsolutePath(),
|
||||
// "/image.jpg",
|
||||
// "image/jpg",
|
||||
// "10000000").execute(client).isSuccess());
|
||||
//
|
||||
// assertTrue(new RefreshFolderOperation(storageManager.getFileByPath("/"),
|
||||
// System.currentTimeMillis(),
|
||||
// false,
|
||||
// true,
|
||||
// storageManager,
|
||||
// user.toPlatformAccount(),
|
||||
// targetContext
|
||||
// ).execute(client).isSuccess());
|
||||
//
|
||||
// OCFile existingFile = storageManager.getFileByPath("/image.jpg");
|
||||
//
|
||||
// Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||
// intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile);
|
||||
// intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile);
|
||||
//
|
||||
// ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||
//
|
||||
// ConflictsResolveDialog.OnConflictDecisionMadeListener listener = decision -> {
|
||||
//
|
||||
// };
|
||||
//
|
||||
// ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
|
||||
// newFile,
|
||||
// UserAccountManagerImpl
|
||||
// .fromContext(targetContext)
|
||||
// .getUser()
|
||||
// );
|
||||
// dialog.showDialog(sut);
|
||||
// dialog.listener = listener;
|
||||
//
|
||||
// getInstrumentation().waitForIdleSync();
|
||||
// shortSleep();
|
||||
//
|
||||
// screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void cancel() {
|
||||
returnCode = false;
|
||||
|
||||
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt",
|
||||
"/newFile.txt",
|
||||
user.getAccountName());
|
||||
|
||||
OCFile existingFile = new OCFile("/newFile.txt");
|
||||
existingFile.setFileLength(1024000);
|
||||
existingFile.setModificationTimestamp(1582019340);
|
||||
|
||||
OCFile newFile = new OCFile("/newFile.txt");
|
||||
newFile.setFileLength(56000);
|
||||
newFile.setModificationTimestamp(1522019340);
|
||||
newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
|
||||
|
||||
FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver());
|
||||
storageManager.saveNewFile(existingFile);
|
||||
|
||||
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId());
|
||||
|
||||
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||
|
||||
sut.listener = decision -> {
|
||||
assertEquals(decision, ConflictsResolveDialog.Decision.CANCEL);
|
||||
returnCode = true;
|
||||
};
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
shortSleep();
|
||||
|
||||
onView(withText("Cancel")).perform(click());
|
||||
|
||||
assertTrue(returnCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void keepExisting() {
|
||||
returnCode = false;
|
||||
|
||||
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt",
|
||||
"/newFile.txt",
|
||||
user.getAccountName());
|
||||
|
||||
OCFile existingFile = new OCFile("/newFile.txt");
|
||||
existingFile.setFileLength(1024000);
|
||||
existingFile.setModificationTimestamp(1582019340);
|
||||
|
||||
OCFile newFile = new OCFile("/newFile.txt");
|
||||
newFile.setFileLength(56000);
|
||||
newFile.setModificationTimestamp(1522019340);
|
||||
newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
|
||||
|
||||
FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver());
|
||||
storageManager.saveNewFile(existingFile);
|
||||
|
||||
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId());
|
||||
|
||||
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||
|
||||
sut.listener = decision -> {
|
||||
assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_SERVER);
|
||||
returnCode = true;
|
||||
};
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
onView(withId(R.id.existing_checkbox)).perform(click());
|
||||
|
||||
DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
|
||||
onView(withText("OK")).perform(click());
|
||||
|
||||
assertTrue(returnCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void keepNew() {
|
||||
returnCode = false;
|
||||
|
||||
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt",
|
||||
"/newFile.txt",
|
||||
user.getAccountName());
|
||||
|
||||
OCFile existingFile = new OCFile("/newFile.txt");
|
||||
existingFile.setFileLength(1024000);
|
||||
existingFile.setModificationTimestamp(1582019340);
|
||||
existingFile.setRemoteId("00000123abc");
|
||||
|
||||
OCFile newFile = new OCFile("/newFile.txt");
|
||||
newFile.setFileLength(56000);
|
||||
newFile.setModificationTimestamp(1522019340);
|
||||
newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
|
||||
|
||||
FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver());
|
||||
storageManager.saveNewFile(existingFile);
|
||||
|
||||
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId());
|
||||
|
||||
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||
|
||||
sut.listener = decision -> {
|
||||
assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_LOCAL);
|
||||
returnCode = true;
|
||||
};
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
onView(withId(R.id.new_checkbox)).perform(click());
|
||||
|
||||
DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
|
||||
onView(withText("OK")).perform(click());
|
||||
|
||||
assertTrue(returnCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void keepBoth() {
|
||||
returnCode = false;
|
||||
|
||||
OCUpload newUpload = new OCUpload(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt",
|
||||
"/newFile.txt",
|
||||
user.getAccountName());
|
||||
|
||||
OCFile existingFile = new OCFile("/newFile.txt");
|
||||
existingFile.setFileLength(1024000);
|
||||
existingFile.setModificationTimestamp(1582019340);
|
||||
|
||||
OCFile newFile = new OCFile("/newFile.txt");
|
||||
newFile.setFileLength(56000);
|
||||
newFile.setModificationTimestamp(1522019340);
|
||||
newFile.setStoragePath(FileStorageUtils.getSavePath(user.getAccountName()) + "/nonEmpty.txt");
|
||||
|
||||
FileDataStorageManager storageManager = new FileDataStorageManager(user, targetContext.getContentResolver());
|
||||
storageManager.saveNewFile(existingFile);
|
||||
|
||||
Intent intent = new Intent(targetContext, ConflictsResolveActivity.class);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_FILE, newFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_EXISTING_FILE, existingFile);
|
||||
intent.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD_ID, newUpload.getUploadId());
|
||||
|
||||
ConflictsResolveActivity sut = activityRule.launchActivity(intent);
|
||||
|
||||
sut.listener = decision -> {
|
||||
assertEquals(decision, ConflictsResolveDialog.Decision.KEEP_BOTH);
|
||||
returnCode = true;
|
||||
};
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
onView(withId(R.id.existing_checkbox)).perform(click());
|
||||
onView(withId(R.id.new_checkbox)).perform(click());
|
||||
|
||||
DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
|
||||
onView(withText("OK")).perform(click());
|
||||
|
||||
assertTrue(returnCode);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
getStorageManager().deleteAllFiles();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class ContactsPreferenceActivityIT : AbstractIT() {
|
||||
@get:Rule
|
||||
var activityRule = IntentsTestRule(ContactsPreferenceActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun openVCF() {
|
||||
val file = getFile("vcard.vcf")
|
||||
val vcfFile = OCFile("/contacts.vcf")
|
||||
vcfFile.storagePath = file.absolutePath
|
||||
|
||||
assertTrue(vcfFile.isDown)
|
||||
|
||||
val intent = Intent()
|
||||
intent.putExtra(ContactsPreferenceActivity.EXTRA_FILE, vcfFile)
|
||||
intent.putExtra(ContactsPreferenceActivity.EXTRA_USER, user)
|
||||
val sut = activityRule.launchActivity(intent)
|
||||
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun openContactsPreference() {
|
||||
val sut = activityRule.launchActivity(null)
|
||||
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl;
|
||||
import com.nextcloud.test.RetryTestRule;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class DrawerActivityIT extends AbstractIT {
|
||||
@Rule public IntentsTestRule<FileDisplayActivity> activityRule = new IntentsTestRule<>(FileDisplayActivity.class,
|
||||
true,
|
||||
false);
|
||||
|
||||
@Rule
|
||||
public final RetryTestRule retryTestRule = new RetryTestRule();
|
||||
|
||||
private static Account account1;
|
||||
private static User user1;
|
||||
private static Account account2;
|
||||
private static String account2Name;
|
||||
private static String account2DisplayName;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
Bundle arguments = androidx.test.platform.app.InstrumentationRegistry.getArguments();
|
||||
Uri baseUrl = Uri.parse(arguments.getString("TEST_SERVER_URL"));
|
||||
|
||||
AccountManager platformAccountManager = AccountManager.get(targetContext);
|
||||
UserAccountManager userAccountManager = UserAccountManagerImpl.fromContext(targetContext);
|
||||
|
||||
for (Account account : platformAccountManager.getAccounts()) {
|
||||
platformAccountManager.removeAccountExplicitly(account);
|
||||
}
|
||||
|
||||
String loginName = "user1";
|
||||
String password = "user1";
|
||||
|
||||
Account temp = new Account(loginName + "@" + baseUrl, MainApp.getAccountType(targetContext));
|
||||
platformAccountManager.addAccountExplicitly(temp, password, null);
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION,
|
||||
Integer.toString(UserAccountManager.ACCOUNT_VERSION));
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0");
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString());
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName); // same as userId
|
||||
|
||||
account1 = userAccountManager.getAccountByName(loginName + "@" + baseUrl);
|
||||
user1 = userAccountManager.getUser(account1.name).orElseThrow(IllegalAccessError::new);
|
||||
|
||||
loginName = "user2";
|
||||
password = "user2";
|
||||
|
||||
temp = new Account(loginName + "@" + baseUrl, MainApp.getAccountType(targetContext));
|
||||
platformAccountManager.addAccountExplicitly(temp, password, null);
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_ACCOUNT_VERSION,
|
||||
Integer.toString(UserAccountManager.ACCOUNT_VERSION));
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_VERSION, "14.0.0.0");
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, baseUrl.toString());
|
||||
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, loginName); // same as userId
|
||||
|
||||
account2 = userAccountManager.getAccountByName(loginName + "@" + baseUrl);
|
||||
account2Name = loginName + "@" + baseUrl;
|
||||
account2DisplayName = "User Two@" + baseUrl;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void switchAccountViaAccountList() {
|
||||
FileDisplayActivity sut = activityRule.launchActivity(null);
|
||||
|
||||
sut.setUser(user1);
|
||||
|
||||
assertEquals(account1, sut.getUser().get().toPlatformAccount());
|
||||
|
||||
onView(withId(R.id.switch_account_button)).perform(click());
|
||||
|
||||
onView(anyOf(withText(account2Name), withText(account2DisplayName))).perform(click());
|
||||
|
||||
waitForIdleSync();
|
||||
|
||||
assertEquals(account2, sut.getUser().get().toPlatformAccount());
|
||||
|
||||
onView(withId(R.id.switch_account_button)).perform(click());
|
||||
onView(withText(account1.name)).perform(click());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Unpublished <unpublished@gmx.net>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.nextcloud.client.onboarding.WhatsNewActivity;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
|
||||
|
||||
import static androidx.test.runner.lifecycle.Stage.RESUMED;
|
||||
|
||||
public class FileDisplayActivityTest extends AbstractIT {
|
||||
@Test
|
||||
public void testSetupToolbar() {
|
||||
try (ActivityScenario<FileDisplayActivity> scenario = ActivityScenario.launch(FileDisplayActivity.class)) {
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
Activity activity =
|
||||
ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED).iterator().next();
|
||||
if (activity instanceof WhatsNewActivity) {
|
||||
activity.onBackPressed();
|
||||
}
|
||||
});
|
||||
scenario.recreate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2019 Kilian Périsset <kilian.perisset@infomaniak.com>
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
//@LargeTest
|
||||
public class FolderPickerActivityIT extends AbstractIT {
|
||||
@Rule
|
||||
public ActivityTestRule<FolderPickerActivity> activityRule =
|
||||
new ActivityTestRule<>(FolderPickerActivity.class);
|
||||
|
||||
@Test
|
||||
public void getActivityFile() {
|
||||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/file.test");
|
||||
origin.setRemotePath("/remotePath/test");
|
||||
|
||||
// Act
|
||||
targetActivity.setFile(origin);
|
||||
OCFile target = targetActivity.getFile();
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(origin, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParentFolder_isNotRootFolder() {
|
||||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/");
|
||||
origin.setFileId(1);
|
||||
origin.setRemotePath("/test/");
|
||||
origin.setStoragePath("/test/");
|
||||
origin.setFolder();
|
||||
|
||||
// Act
|
||||
targetActivity.setFile(origin);
|
||||
OCFile target = targetActivity.getCurrentFolder();
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(origin, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParentFolder_isRootFolder() {
|
||||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/");
|
||||
origin.setFileId(1);
|
||||
origin.setRemotePath("/");
|
||||
origin.setStoragePath("/");
|
||||
origin.setFolder();
|
||||
|
||||
// Act
|
||||
targetActivity.setFile(origin);
|
||||
OCFile target = targetActivity.getCurrentFolder();
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(origin, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullFile() {
|
||||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile rootFolder = targetActivity.getStorageManager().getFileByPath(OCFile.ROOT_PATH);
|
||||
|
||||
// Act
|
||||
targetActivity.setFile(null);
|
||||
OCFile target = targetActivity.getCurrentFolder();
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(rootFolder, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getParentFolder() {
|
||||
// Arrange
|
||||
FolderPickerActivity targetActivity = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/file.test");
|
||||
origin.setRemotePath("/test/file.test");
|
||||
|
||||
OCFile target = new OCFile("/test/");
|
||||
|
||||
// Act
|
||||
targetActivity.setFile(origin);
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(origin, target);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void open() {
|
||||
FolderPickerActivity sut = activityRule.getActivity();
|
||||
OCFile origin = new OCFile("/test/file.txt");
|
||||
sut.setFile(origin);
|
||||
|
||||
sut.runOnUiThread(() -> {
|
||||
sut.findViewById(R.id.folder_picker_btn_copy).requestFocus();
|
||||
});
|
||||
waitForIdleSync();
|
||||
screenshot(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testMoveOrCopy() {
|
||||
Intent intent = new Intent();
|
||||
FolderPickerActivity targetActivity = activityRule.launchActivity(intent);
|
||||
|
||||
waitForIdleSync();
|
||||
screenshot(targetActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testChooseLocationAction() {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(FolderPickerActivity.EXTRA_ACTION, FolderPickerActivity.CHOOSE_LOCATION);
|
||||
FolderPickerActivity targetActivity = activityRule.launchActivity(intent);
|
||||
|
||||
waitForIdleSync();
|
||||
screenshot(targetActivity);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.lib.common.Quota;
|
||||
import com.owncloud.android.lib.common.UserInfo;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
public class ManageAccountsActivityIT extends AbstractIT {
|
||||
@Rule
|
||||
public IntentsTestRule<ManageAccountsActivity> activityRule = new IntentsTestRule<>(ManageAccountsActivity.class,
|
||||
true,
|
||||
false);
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void open() {
|
||||
Activity sut = activityRule.launchActivity(null);
|
||||
|
||||
shortSleep();
|
||||
|
||||
screenshot(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void userInfoDetail() {
|
||||
ManageAccountsActivity sut = activityRule.launchActivity(null);
|
||||
|
||||
User user = sut.accountManager.getUser();
|
||||
|
||||
UserInfo userInfo = new UserInfo("test",
|
||||
true,
|
||||
"Test User",
|
||||
"test@nextcloud.com",
|
||||
"+49 123 456",
|
||||
"Address 123, Berlin",
|
||||
"https://www.nextcloud.com",
|
||||
"https://twitter.com/Nextclouders",
|
||||
new Quota(),
|
||||
new ArrayList<>());
|
||||
|
||||
sut.showUser(user, userInfo);
|
||||
|
||||
shortSleep();
|
||||
shortSleep();
|
||||
|
||||
screenshot(getCurrentActivity());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity
|
||||
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.test.core.app.launchActivity
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.IdlingRegistry
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import com.owncloud.android.utils.EspressoIdlingResource
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.lib.resources.notifications.models.Action
|
||||
import com.owncloud.android.lib.resources.notifications.models.Notification
|
||||
import com.owncloud.android.lib.resources.notifications.models.RichObject
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.GregorianCalendar
|
||||
|
||||
class NotificationsActivityIT : AbstractIT() {
|
||||
private val testClassName = "com.owncloud.android.ui.activity.NotificationsActivityIT"
|
||||
|
||||
@Before
|
||||
fun registerIdlingResource() {
|
||||
IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
|
||||
}
|
||||
|
||||
@After
|
||||
fun unregisterIdlingResource() {
|
||||
IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun empty() {
|
||||
launchActivity<NotificationsActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.populateList(ArrayList())
|
||||
EspressoIdlingResource.decrement()
|
||||
val screenShotName = createName(testClassName + "_" + "empty", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun showNotifications() {
|
||||
val date = GregorianCalendar()
|
||||
date.set(2005, 4, 17, 10, 35, 30) // random date
|
||||
|
||||
val notifications = ArrayList<Notification>()
|
||||
notifications.add(
|
||||
Notification(
|
||||
1,
|
||||
"files",
|
||||
"user",
|
||||
date.time,
|
||||
"objectType",
|
||||
"objectId",
|
||||
"App recommendation: Tasks",
|
||||
"SubjectRich",
|
||||
HashMap<String, RichObject>(),
|
||||
"Sync tasks from various devices with your Nextcloud and edit them online.",
|
||||
"MessageRich",
|
||||
HashMap<String, RichObject>(),
|
||||
"link",
|
||||
"icon",
|
||||
ArrayList<Action>()
|
||||
)
|
||||
)
|
||||
|
||||
val actions = ArrayList<Action>().apply {
|
||||
add(Action("Send usage", "link", "url", true))
|
||||
add(Action("Not now", "link", "url", false))
|
||||
}
|
||||
|
||||
notifications.add(
|
||||
Notification(
|
||||
1,
|
||||
"files",
|
||||
"user",
|
||||
date.time,
|
||||
"objectType",
|
||||
"objectId",
|
||||
"Help improve Nextcloud",
|
||||
"SubjectRich",
|
||||
HashMap<String, RichObject>(),
|
||||
"Do you want to help us to improve Nextcloud" +
|
||||
" by providing some anonymize data about your setup and usage?",
|
||||
"MessageRich",
|
||||
HashMap<String, RichObject>(),
|
||||
"link",
|
||||
"icon",
|
||||
actions
|
||||
)
|
||||
)
|
||||
|
||||
val moreAction = ArrayList<Action>().apply {
|
||||
add(Action("Send usage", "link", "url", true))
|
||||
add(Action("Not now", "link", "url", false))
|
||||
add(Action("third action", "link", "url", false))
|
||||
add(Action("Delay", "link", "url", false))
|
||||
}
|
||||
|
||||
notifications.add(
|
||||
Notification(
|
||||
2,
|
||||
"files",
|
||||
"user",
|
||||
date.time,
|
||||
"objectType",
|
||||
"objectId",
|
||||
"Help improve Nextcloud",
|
||||
"SubjectRich",
|
||||
HashMap<String, RichObject>(),
|
||||
"Do you want to help us to improve Nextcloud by providing some anonymize data about your setup and " +
|
||||
"usage?",
|
||||
"MessageRich",
|
||||
HashMap<String, RichObject>(),
|
||||
"link",
|
||||
"icon",
|
||||
moreAction
|
||||
)
|
||||
)
|
||||
|
||||
launchActivity<NotificationsActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.populateList(notifications)
|
||||
EspressoIdlingResource.decrement()
|
||||
val screenShotName = createName(testClassName + "_" + "showNotifications", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun error() {
|
||||
launchActivity<NotificationsActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.setEmptyContent("Error", "Error! Please try again later!")
|
||||
EspressoIdlingResource.decrement()
|
||||
val screenShotName = createName(testClassName + "_" + "error", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.test.espresso.Espresso
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class PassCodeActivityIT : AbstractIT() {
|
||||
@get:Rule
|
||||
var activityRule = IntentsTestRule(PassCodeActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun check() {
|
||||
val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_CHECK))
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
sut.runOnUiThread { sut.binding.txt0.clearFocus() }
|
||||
Espresso.closeSoftKeyboard()
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun request() {
|
||||
val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_REQUEST_WITH_RESULT))
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
sut.runOnUiThread { sut.binding.txt0.clearFocus() }
|
||||
Espresso.closeSoftKeyboard()
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun delete() {
|
||||
val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_CHECK_WITH_RESULT))
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
sut.runOnUiThread { sut.binding.txt0.clearFocus() }
|
||||
Espresso.closeSoftKeyboard()
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class ReceiveExternalFilesActivityIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val activityRule = IntentsTestRule(ReceiveExternalFilesActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun open() {
|
||||
val sut: Activity = activityRule.launchActivity(null)
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun openMultiAccount() {
|
||||
val secondAccount = createAccount("secondtest@https://nextcloud.localhost")
|
||||
open()
|
||||
removeAccount(secondAccount)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.GrantStoragePermissionRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.utils.FileStorageUtils
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class UploadFilesActivityIT : AbstractIT() {
|
||||
@get:Rule
|
||||
var activityRule = IntentsTestRule(UploadFilesActivity::class.java, true, false)
|
||||
|
||||
@get:Rule
|
||||
var permissionRule = GrantStoragePermissionRule.grant()
|
||||
|
||||
private val directories = listOf("A", "B", "C", "D")
|
||||
.map { File("${FileStorageUtils.getTemporalPath(account.name)}${File.separator}$it") }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
directories.forEach { it.mkdirs() }
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
directories.forEach { it.deleteRecursively() }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun noneSelected() {
|
||||
val sut: UploadFilesActivity = activityRule.launchActivity(null)
|
||||
|
||||
sut.runOnUiThread {
|
||||
sut.fileListFragment.setFiles(
|
||||
directories +
|
||||
listOf(
|
||||
File("1.txt"),
|
||||
File("2.pdf"),
|
||||
File("3.mp3")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
longSleep()
|
||||
|
||||
screenshot(sut.fileListFragment.binding.listRoot)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun localFolderPickerMode() {
|
||||
val sut: UploadFilesActivity = activityRule.launchActivity(
|
||||
Intent().apply {
|
||||
putExtra(
|
||||
UploadFilesActivity.KEY_LOCAL_FOLDER_PICKER_MODE,
|
||||
true
|
||||
)
|
||||
putExtra(
|
||||
UploadFilesActivity.REQUEST_CODE_KEY,
|
||||
FileDisplayActivity.REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
sut.runOnUiThread {
|
||||
sut.fileListFragment.setFiles(
|
||||
directories
|
||||
)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
fun fileSelected() {
|
||||
val sut: UploadFilesActivity = activityRule.launchActivity(null)
|
||||
|
||||
// TODO select one
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.lib.common.UserInfo;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
public class UserInfoActivityIT extends AbstractIT {
|
||||
@Rule
|
||||
public IntentsTestRule<UserInfoActivity> activityRule = new IntentsTestRule<>(UserInfoActivity.class,
|
||||
true,
|
||||
false);
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void fullUserInfoDetail() {
|
||||
final Intent intent = new Intent(targetContext, UserInfoActivity.class);
|
||||
intent.putExtra(UserInfoActivity.KEY_ACCOUNT, user);
|
||||
UserInfo userInfo = new UserInfo("test",
|
||||
true,
|
||||
"Firstname Familyname",
|
||||
"oss@rocks.com",
|
||||
"+49 7613 672 255",
|
||||
"Awesome Place Av.",
|
||||
"https://www.nextcloud.com",
|
||||
"nextclouders",
|
||||
null,
|
||||
null
|
||||
);
|
||||
intent.putExtra(UserInfoActivity.KEY_USER_DATA, userInfo);
|
||||
UserInfoActivity sut = activityRule.launchActivity(intent);
|
||||
|
||||
shortSleep();
|
||||
shortSleep();
|
||||
|
||||
screenshot(sut);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.adapter
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class OCFileListAdapterIT : AbstractIT() {
|
||||
@Test
|
||||
fun testParseMedia() {
|
||||
// empty start
|
||||
storageManager.deleteAllFiles()
|
||||
|
||||
val startDate: Long = 0
|
||||
val endDate: Long = 3642752043
|
||||
assertEquals(0, storageManager.getGalleryItems(startDate, endDate).size)
|
||||
|
||||
// create dummy files
|
||||
OCFile("/test.txt").apply {
|
||||
mimeType = "text/plain"
|
||||
}.let {
|
||||
storageManager.saveFile(it)
|
||||
}
|
||||
|
||||
OCFile("/image.png").apply {
|
||||
mimeType = "image/png"
|
||||
modificationTimestamp = 1000000
|
||||
}.let {
|
||||
storageManager.saveFile(it)
|
||||
}
|
||||
|
||||
OCFile("/image2.png").apply {
|
||||
mimeType = "image/png"
|
||||
modificationTimestamp = 1000050
|
||||
}.let {
|
||||
storageManager.saveFile(it)
|
||||
}
|
||||
|
||||
OCFile("/video.mpg").apply {
|
||||
mimeType = "video/mpg"
|
||||
modificationTimestamp = 1000045
|
||||
}.let {
|
||||
storageManager.saveFile(it)
|
||||
}
|
||||
|
||||
OCFile("/video2.avi").apply {
|
||||
mimeType = "video/avi"
|
||||
modificationTimestamp = endDate + 10
|
||||
}.let {
|
||||
storageManager.saveFile(it)
|
||||
}
|
||||
|
||||
// list of remoteFiles
|
||||
assertEquals(5, storageManager.allFiles.size)
|
||||
assertEquals(3, storageManager.getGalleryItems(startDate, endDate).size)
|
||||
assertEquals(4, storageManager.allGalleryItems.size)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,630 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.dialog;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Intent;
|
||||
import android.net.http.SslCertificate;
|
||||
import android.net.http.SslError;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.gson.Gson;
|
||||
import com.nextcloud.android.common.ui.color.ColorUtil;
|
||||
import com.nextcloud.android.common.ui.theme.MaterialSchemes;
|
||||
import com.nextcloud.android.common.ui.theme.MaterialSchemesImpl;
|
||||
import com.nextcloud.android.lib.resources.profile.Action;
|
||||
import com.nextcloud.android.lib.resources.profile.HoverCard;
|
||||
import com.nextcloud.client.account.RegisteredUser;
|
||||
import com.nextcloud.client.account.Server;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.nextcloud.client.account.UserAccountManager;
|
||||
import com.nextcloud.client.device.DeviceInfo;
|
||||
import com.nextcloud.client.documentscan.AppScanOptionalFeature;
|
||||
import com.nextcloud.ui.ChooseAccountDialogFragment;
|
||||
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
|
||||
import com.nextcloud.utils.EditorUtils;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.Creator;
|
||||
import com.owncloud.android.lib.common.DirectEditing;
|
||||
import com.owncloud.android.lib.common.Editor;
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
|
||||
import com.owncloud.android.lib.resources.status.OCCapability;
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
||||
import com.owncloud.android.lib.resources.users.Status;
|
||||
import com.owncloud.android.lib.resources.users.StatusType;
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||
import com.owncloud.android.ui.fragment.OCFileListBottomSheetActions;
|
||||
import com.owncloud.android.ui.fragment.OCFileListBottomSheetDialog;
|
||||
import com.owncloud.android.ui.fragment.ProfileBottomSheetDialog;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
import com.owncloud.android.utils.theme.CapabilityUtils;
|
||||
import com.owncloud.android.utils.theme.MaterialSchemesProvider;
|
||||
import com.owncloud.android.utils.theme.ViewThemeUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.activity.result.contract.ActivityResultContract;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
import kotlin.Unit;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
public class DialogFragmentIT extends AbstractIT {
|
||||
|
||||
private final String SERVER_URL = "https://nextcloud.localhost";
|
||||
|
||||
@Rule public IntentsTestRule<FileDisplayActivity> activityRule =
|
||||
new IntentsTestRule<>(FileDisplayActivity.class, true, false);
|
||||
|
||||
private FileDisplayActivity getFileDisplayActivity() {
|
||||
Intent intent = new Intent(targetContext, FileDisplayActivity.class);
|
||||
return activityRule.launchActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void quitLooperIfNeeded() {
|
||||
if (Looper.myLooper() != null) {
|
||||
Looper.myLooper().quitSafely();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testRenameFileDialog() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(new OCFile("/Test/"),
|
||||
new OCFile("/"));
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testLoadingDialog() {
|
||||
LoadingDialog dialog = LoadingDialog.newInstance("Wait…");
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testConfirmationDialogWithOneAction() {
|
||||
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[]{}, R.string.filedetails_sync_file, R.string.common_ok, -1, -1);
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testConfirmationDialogWithTwoAction() {
|
||||
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[]{}, R.string.filedetails_sync_file, R.string.common_ok, R.string.common_cancel, -1);
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testConfirmationDialogWithThreeAction() {
|
||||
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[]{}, R.string.filedetails_sync_file, R.string.common_ok, R.string.common_cancel, R.string.common_confirm);
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testConfirmationDialogWithThreeActionRTL() {
|
||||
enableRTL();
|
||||
|
||||
ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_list_empty_text_auto_upload, new String[] { }, -1, R.string.common_ok, R.string.common_cancel, R.string.common_confirm);
|
||||
showDialog(dialog);
|
||||
|
||||
resetLocale();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testRemoveFileDialog() {
|
||||
RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(new OCFile("/Test.md"));
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testRemoveFilesDialog() {
|
||||
ArrayList<OCFile> toDelete = new ArrayList<>();
|
||||
toDelete.add(new OCFile("/Test.md"));
|
||||
toDelete.add(new OCFile("/Document.odt"));
|
||||
|
||||
RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(toDelete);
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testRemoveFolderDialog() {
|
||||
RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(new OCFile("/Folder/"));
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testRemoveFoldersDialog() {
|
||||
ArrayList<OCFile> toDelete = new ArrayList<>();
|
||||
toDelete.add(new OCFile("/Folder/"));
|
||||
toDelete.add(new OCFile("/Documents/"));
|
||||
|
||||
RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(toDelete);
|
||||
showDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testNewFolderDialog() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
CreateFolderDialogFragment sut = CreateFolderDialogFragment.newInstance(new OCFile("/"));
|
||||
showDialog(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testEnforcedPasswordDialog() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
SharePasswordDialogFragment sut = SharePasswordDialogFragment.newInstance(new OCFile("/"), true, false);
|
||||
showDialog(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testOptionalPasswordDialog() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
SharePasswordDialogFragment sut = SharePasswordDialogFragment.newInstance(new OCFile("/"), true, true);
|
||||
showDialog(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testAccountChooserDialog() throws AccountUtils.AccountNotFoundException {
|
||||
FileDisplayActivity activity = getFileDisplayActivity();
|
||||
UserAccountManager userAccountManager = activity.getUserAccountManager();
|
||||
AccountManager accountManager = AccountManager.get(targetContext);
|
||||
for (Account account : accountManager.getAccountsByType(MainApp.getAccountType(targetContext))) {
|
||||
accountManager.removeAccountExplicitly(account);
|
||||
}
|
||||
|
||||
Account newAccount = new Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext));
|
||||
accountManager.addAccountExplicitly(newAccount, "password", null);
|
||||
accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_OC_BASE_URL, SERVER_URL);
|
||||
accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_USER_ID, "test");
|
||||
accountManager.setAuthToken(newAccount, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password");
|
||||
User newUser = userAccountManager.getUser(newAccount.name).orElseThrow(RuntimeException::new);
|
||||
userAccountManager.setCurrentOwnCloudAccount(newAccount.name);
|
||||
|
||||
Account newAccount2 = new Account("user1@nextcloud.localhost", MainApp.getAccountType(targetContext));
|
||||
accountManager.addAccountExplicitly(newAccount2, "password", null);
|
||||
accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_OC_BASE_URL, SERVER_URL);
|
||||
accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_USER_ID, "user1");
|
||||
accountManager.setUserData(newAccount2, AccountUtils.Constants.KEY_OC_VERSION, "20.0.0");
|
||||
accountManager.setAuthToken(newAccount2, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password");
|
||||
|
||||
FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(newUser,
|
||||
targetContext.getContentResolver());
|
||||
|
||||
OCCapability capability = new OCCapability();
|
||||
capability.setUserStatus(CapabilityBooleanType.TRUE);
|
||||
capability.setUserStatusSupportsEmoji(CapabilityBooleanType.TRUE);
|
||||
fileDataStorageManager.saveCapabilities(capability);
|
||||
|
||||
ChooseAccountDialogFragment sut =
|
||||
ChooseAccountDialogFragment.newInstance(new RegisteredUser(newAccount,
|
||||
new OwnCloudAccount(newAccount, targetContext),
|
||||
new Server(URI.create(SERVER_URL),
|
||||
OwnCloudVersion.nextcloud_20)));
|
||||
showDialog(activity, sut);
|
||||
|
||||
activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.DND,
|
||||
"Busy fixing 🐛…",
|
||||
"",
|
||||
-1),
|
||||
targetContext));
|
||||
waitForIdleSync();
|
||||
shortSleep();
|
||||
screenshot(sut, "dnd");
|
||||
|
||||
activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.ONLINE,
|
||||
"",
|
||||
"",
|
||||
-1),
|
||||
targetContext));
|
||||
waitForIdleSync();
|
||||
shortSleep();
|
||||
screenshot(sut, "online");
|
||||
|
||||
activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.ONLINE,
|
||||
"Let's have some fun",
|
||||
"🎉",
|
||||
-1),
|
||||
targetContext));
|
||||
waitForIdleSync();
|
||||
shortSleep();
|
||||
screenshot(sut, "fun");
|
||||
|
||||
activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.OFFLINE, "", "", -1), targetContext));
|
||||
waitForIdleSync();
|
||||
shortSleep();
|
||||
screenshot(sut, "offline");
|
||||
|
||||
activity.runOnUiThread(() -> sut.setStatus(new Status(StatusType.AWAY, "Vacation", "🌴", -1), targetContext));
|
||||
waitForIdleSync();
|
||||
shortSleep();
|
||||
screenshot(sut, "away");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testAccountChooserDialogWithStatusDisabled() throws AccountUtils.AccountNotFoundException {
|
||||
AccountManager accountManager = AccountManager.get(targetContext);
|
||||
for (Account account : accountManager.getAccounts()) {
|
||||
accountManager.removeAccountExplicitly(account);
|
||||
}
|
||||
|
||||
Account newAccount = new Account("test@https://nextcloud.localhost", MainApp.getAccountType(targetContext));
|
||||
accountManager.addAccountExplicitly(newAccount, "password", null);
|
||||
accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_OC_BASE_URL, SERVER_URL);
|
||||
accountManager.setUserData(newAccount, AccountUtils.Constants.KEY_USER_ID, "test");
|
||||
accountManager.setAuthToken(newAccount, AccountTypeUtils.getAuthTokenTypePass(newAccount.type), "password");
|
||||
|
||||
FileDisplayActivity fda = getFileDisplayActivity();
|
||||
UserAccountManager userAccountManager = fda.getUserAccountManager();
|
||||
User newUser = userAccountManager.getUser(newAccount.name).get();
|
||||
FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(newUser,
|
||||
targetContext.getContentResolver());
|
||||
|
||||
OCCapability capability = new OCCapability();
|
||||
capability.setUserStatus(CapabilityBooleanType.FALSE);
|
||||
|
||||
fileDataStorageManager.saveCapabilities(capability);
|
||||
|
||||
ChooseAccountDialogFragment sut =
|
||||
ChooseAccountDialogFragment.newInstance(new RegisteredUser(newAccount,
|
||||
new OwnCloudAccount(newAccount, targetContext),
|
||||
new Server(URI.create(SERVER_URL),
|
||||
OwnCloudVersion.nextcloud_20)));
|
||||
showDialog(fda, sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testBottomSheet() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
|
||||
OCFileListBottomSheetActions action = new OCFileListBottomSheetActions() {
|
||||
|
||||
@Override
|
||||
public void createFolder() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadFromApp() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadFiles() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newDocument() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newSpreadsheet() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newPresentation() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void directCameraUpload() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanDocUpload() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showTemplate(Creator creator, String headline) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRichWorkspace() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
DeviceInfo info = new DeviceInfo();
|
||||
OCFile ocFile = new OCFile("/test.md");
|
||||
|
||||
Intent intent = new Intent(targetContext, FileDisplayActivity.class);
|
||||
FileDisplayActivity fda = activityRule.launchActivity(intent);
|
||||
|
||||
// add direct editing info
|
||||
DirectEditing directEditing = new DirectEditing();
|
||||
directEditing.getCreators().put("1", new Creator("1",
|
||||
"text",
|
||||
"text file",
|
||||
".md",
|
||||
"application/octet-stream",
|
||||
false));
|
||||
|
||||
directEditing.getCreators().put("2", new Creator("2",
|
||||
"md",
|
||||
"markdown file",
|
||||
".md",
|
||||
"application/octet-stream",
|
||||
false));
|
||||
|
||||
directEditing.getEditors().put("text",
|
||||
new Editor("1",
|
||||
"Text",
|
||||
new ArrayList<>(Collections.singletonList(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN)),
|
||||
new ArrayList<>(),
|
||||
false));
|
||||
|
||||
String json = new Gson().toJson(directEditing);
|
||||
|
||||
new ArbitraryDataProviderImpl(targetContext).storeOrUpdateKeyValue(user.getAccountName(),
|
||||
ArbitraryDataProvider.DIRECT_EDITING,
|
||||
json);
|
||||
|
||||
// activate templates
|
||||
OCCapability capability = fda.getCapabilities();
|
||||
capability.setRichDocuments(CapabilityBooleanType.TRUE);
|
||||
capability.setRichDocumentsDirectEditing(CapabilityBooleanType.TRUE);
|
||||
capability.setRichDocumentsTemplatesAvailable(CapabilityBooleanType.TRUE);
|
||||
capability.setAccountName(user.getAccountName());
|
||||
|
||||
CapabilityUtils.updateCapability(capability);
|
||||
|
||||
AppScanOptionalFeature appScanOptionalFeature = new AppScanOptionalFeature() {
|
||||
@NonNull
|
||||
@Override
|
||||
public ActivityResultContract<Unit, String> getScanContract() {
|
||||
throw new UnsupportedOperationException("Document scan is not available");
|
||||
}
|
||||
};
|
||||
|
||||
MaterialSchemesProvider materialSchemesProvider = new MaterialSchemesProvider() {
|
||||
@NonNull
|
||||
@Override
|
||||
public MaterialSchemes getMaterialSchemesForUser(@NonNull User user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MaterialSchemes getMaterialSchemesForCapability(@NonNull OCCapability capability) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MaterialSchemes getMaterialSchemesForCurrentUser() {
|
||||
return new MaterialSchemesImpl(R.color.primary, false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MaterialSchemes getDefaultMaterialSchemes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MaterialSchemes getMaterialSchemesForPrimaryBackground() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
ViewThemeUtils viewThemeUtils = new ViewThemeUtils(materialSchemesProvider.getMaterialSchemesForCurrentUser(),
|
||||
new ColorUtil(targetContext));
|
||||
|
||||
EditorUtils editorUtils = new EditorUtils(new ArbitraryDataProviderImpl(targetContext));
|
||||
|
||||
|
||||
OCFileListBottomSheetDialog sut = new OCFileListBottomSheetDialog(fda,
|
||||
action,
|
||||
info,
|
||||
user,
|
||||
ocFile,
|
||||
fda.themeUtils,
|
||||
viewThemeUtils,
|
||||
editorUtils,
|
||||
appScanOptionalFeature);
|
||||
|
||||
fda.runOnUiThread(sut::show);
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
shortSleep();
|
||||
|
||||
sut.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
shortSleep();
|
||||
|
||||
ViewGroup viewGroup = sut.getWindow().findViewById(android.R.id.content);
|
||||
hideCursors(viewGroup);
|
||||
|
||||
screenshot(Objects.requireNonNull(sut.getWindow()).getDecorView());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testProfileBottomSheet() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
|
||||
// Fixed values for HoverCard
|
||||
List<Action> actions = new ArrayList<>();
|
||||
actions.add(new Action("profile",
|
||||
"View profile",
|
||||
"https://dev.nextcloud.com/core/img/actions/profile.svg",
|
||||
"https://dev.nextcloud.com/index.php/u/christine"));
|
||||
actions.add(new Action("core",
|
||||
"christine.scott@nextcloud.com",
|
||||
"https://dev.nextcloud.com/core/img/actions/mail.svg",
|
||||
"mailto:christine.scott@nextcloud.com"));
|
||||
|
||||
actions.add(new Action("spreed",
|
||||
"Talk to Christine",
|
||||
"https://dev.nextcloud.com/apps/spreed/img/app-dark.svg",
|
||||
"https://dev.nextcloud.com/apps/spreed/?callUser=christine"
|
||||
));
|
||||
|
||||
HoverCard hoverCard = new HoverCard("christine", "Christine Scott", actions);
|
||||
|
||||
// show dialog
|
||||
Intent intent = new Intent(targetContext, FileDisplayActivity.class);
|
||||
FileDisplayActivity fda = activityRule.launchActivity(intent);
|
||||
|
||||
ProfileBottomSheetDialog sut = new ProfileBottomSheetDialog(fda,
|
||||
user,
|
||||
hoverCard,
|
||||
fda.viewThemeUtils);
|
||||
|
||||
fda.runOnUiThread(sut::show);
|
||||
|
||||
waitForIdleSync();
|
||||
|
||||
screenshot(sut.getWindow().getDecorView());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testSslUntrustedCertDialog() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
|
||||
final SslCertificate certificate = new SslCertificate("foo", "bar", "2022/01/10", "2022/01/30");
|
||||
final SslError sslError = new SslError(SslError.SSL_UNTRUSTED, certificate);
|
||||
|
||||
final SslErrorHandler handler = Mockito.mock(SslErrorHandler.class);
|
||||
|
||||
SslUntrustedCertDialog sut = SslUntrustedCertDialog.newInstanceForEmptySslError(sslError, handler);
|
||||
showDialog(sut);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testStoragePermissionDialog() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
|
||||
StoragePermissionDialogFragment sut = StoragePermissionDialogFragment.Companion.newInstance(false);
|
||||
showDialog(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void testFileActionsBottomSheet() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
|
||||
OCFile ocFile = new OCFile("/test.md");
|
||||
final FileActionsBottomSheet sut = FileActionsBottomSheet.newInstance(ocFile, false);
|
||||
showDialog(sut);
|
||||
}
|
||||
|
||||
private FileDisplayActivity showDialog(DialogFragment dialog) {
|
||||
Intent intent = new Intent(targetContext, FileDisplayActivity.class);
|
||||
|
||||
FileDisplayActivity sut = activityRule.getActivity();
|
||||
|
||||
if (sut == null) {
|
||||
sut = activityRule.launchActivity(intent);
|
||||
}
|
||||
|
||||
return showDialog(sut, dialog);
|
||||
}
|
||||
|
||||
private FileDisplayActivity showDialog(FileDisplayActivity sut, DialogFragment dialog) {
|
||||
dialog.show(sut.getSupportFragmentManager(), "");
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
shortSleep();
|
||||
|
||||
ViewGroup viewGroup = dialog.requireDialog().getWindow().findViewById(android.R.id.content);
|
||||
hideCursors(viewGroup);
|
||||
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
|
||||
return sut;
|
||||
}
|
||||
|
||||
private void hideCursors(ViewGroup viewGroup) {
|
||||
for (int i = 0; i < viewGroup.getChildCount(); i++) {
|
||||
View child = viewGroup.getChildAt(i);
|
||||
|
||||
if (child instanceof ViewGroup) {
|
||||
hideCursors((ViewGroup) child);
|
||||
}
|
||||
|
||||
if (child instanceof TextView) {
|
||||
((TextView) child).setCursorVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.dialog
|
||||
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Assert
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class SendFilesDialogTest : AbstractIT() {
|
||||
companion object {
|
||||
private val FILES_SAME_TYPE = setOf(
|
||||
OCFile("/1.jpg").apply {
|
||||
mimeType = "image/jpg"
|
||||
},
|
||||
OCFile("/2.jpg").apply {
|
||||
mimeType = "image/jpg"
|
||||
}
|
||||
)
|
||||
private val FILES_MIXED_TYPE = setOf(
|
||||
OCFile("/1.jpg").apply {
|
||||
mimeType = "image/jpg"
|
||||
},
|
||||
OCFile("/2.pdf").apply {
|
||||
mimeType = "application/pdf"
|
||||
},
|
||||
OCFile("/3.png").apply {
|
||||
mimeType = "image/png"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
private fun showDialog(files: Set<OCFile>): SendFilesDialog {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
val fm: FragmentManager = activity.supportFragmentManager
|
||||
val ft = fm.beginTransaction()
|
||||
ft.addToBackStack(null)
|
||||
|
||||
val sut = SendFilesDialog.newInstance(files)
|
||||
sut.show(ft, "TAG_SEND_SHARE_DIALOG")
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
shortSleep()
|
||||
|
||||
return sut
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showDialog() {
|
||||
val sut = showDialog(FILES_SAME_TYPE)
|
||||
val recyclerview: RecyclerView = sut.requireDialog().findViewById(R.id.send_button_recycler_view)
|
||||
Assert.assertNotNull("Adapter is null", recyclerview.adapter)
|
||||
Assert.assertNotEquals("Send button list is empty", 0, recyclerview.adapter!!.itemCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showDialog_Screenshot() {
|
||||
val sut = showDialog(FILES_SAME_TYPE)
|
||||
sut.requireDialog().window?.decorView.let { screenshot(it) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showDialogDifferentTypes() {
|
||||
val sut = showDialog(FILES_MIXED_TYPE)
|
||||
val recyclerview: RecyclerView = sut.requireDialog().findViewById(R.id.send_button_recycler_view)
|
||||
Assert.assertNotNull("Adapter is null", recyclerview.adapter)
|
||||
Assert.assertNotEquals("Send button list is empty", 0, recyclerview.adapter!!.itemCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showDialogDifferentTypes_Screenshot() {
|
||||
val sut = showDialog(FILES_MIXED_TYPE)
|
||||
sut.requireDialog().window?.decorView.let { screenshot(it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.dialog
|
||||
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.resources.status.OCCapability
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class SendShareDialogTest : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showDialog() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
val fm: FragmentManager = activity.supportFragmentManager
|
||||
val ft = fm.beginTransaction()
|
||||
ft.addToBackStack(null)
|
||||
|
||||
val file = OCFile("/1.jpg").apply {
|
||||
mimeType = "image/jpg"
|
||||
}
|
||||
|
||||
val sut = SendShareDialog.newInstance(file, false, OCCapability())
|
||||
sut.show(ft, "TAG_SEND_SHARE_DIALOG")
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
sut.requireDialog().window?.decorView.let { screenshot(it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.dialog
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class SetupEncryptionDialogFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showMnemonic() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
val sut = SetupEncryptionDialogFragment.newInstance(user, 0)
|
||||
|
||||
sut.show(activity.supportFragmentManager, "1")
|
||||
|
||||
val keyWords = arrayListOf(
|
||||
"ability",
|
||||
"able",
|
||||
"about",
|
||||
"above",
|
||||
"absent",
|
||||
"absorb",
|
||||
"abstract",
|
||||
"absurd",
|
||||
"abuse",
|
||||
"access",
|
||||
"accident",
|
||||
"account",
|
||||
"accuse"
|
||||
)
|
||||
|
||||
shortSleep()
|
||||
|
||||
runOnUiThread {
|
||||
sut.setMnemonic(keyWords)
|
||||
sut.showMnemonicInfo()
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut.requireDialog().window!!.decorView)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun error() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
val sut = SetupEncryptionDialogFragment.newInstance(user, 0)
|
||||
|
||||
sut.show(activity.supportFragmentManager, "1")
|
||||
|
||||
shortSleep()
|
||||
|
||||
runOnUiThread {
|
||||
sut.errorSavingKeys()
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut.requireDialog().window!!.decorView)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.dialog;
|
||||
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
public class SyncFileNotEnoughSpaceDialogFragmentTest extends AbstractIT {
|
||||
@Rule public IntentsTestRule<FileDisplayActivity> activityRule = new IntentsTestRule<>(FileDisplayActivity.class,
|
||||
true,
|
||||
false);
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void showNotEnoughSpaceDialogForFolder() {
|
||||
FileDisplayActivity test = activityRule.launchActivity(null);
|
||||
OCFile ocFile = new OCFile("/Document/");
|
||||
ocFile.setFileLength(5000000);
|
||||
ocFile.setFolder();
|
||||
|
||||
SyncFileNotEnoughSpaceDialogFragment dialog = SyncFileNotEnoughSpaceDialogFragment.newInstance(ocFile, 1000);
|
||||
dialog.show(test.getListOfFilesFragment().getFragmentManager(), "1");
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void showNotEnoughSpaceDialogForFile() {
|
||||
FileDisplayActivity test = activityRule.launchActivity(null);
|
||||
OCFile ocFile = new OCFile("/Video.mp4");
|
||||
ocFile.setFileLength(1000000);
|
||||
|
||||
SyncFileNotEnoughSpaceDialogFragment dialog = SyncFileNotEnoughSpaceDialogFragment.newInstance(ocFile, 2000);
|
||||
dialog.show(test.getListOfFilesFragment().getFragmentManager(), "2");
|
||||
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.lib.resources.users.StatusType
|
||||
import com.owncloud.android.ui.TextDrawable
|
||||
import com.owncloud.android.utils.BitmapUtils
|
||||
import com.owncloud.android.utils.DisplayUtils
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class AvatarIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showAvatars() {
|
||||
val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius)
|
||||
val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext)
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val fragment = AvatarTestFragment()
|
||||
|
||||
sut.addFragment(fragment)
|
||||
|
||||
runOnUiThread {
|
||||
fragment.addAvatar("Admin", avatarRadius, width, targetContext)
|
||||
fragment.addAvatar("Test Server Admin", avatarRadius, width, targetContext)
|
||||
fragment.addAvatar("Cormier Paulette", avatarRadius, width, targetContext)
|
||||
fragment.addAvatar("winston brent", avatarRadius, width, targetContext)
|
||||
fragment.addAvatar("Baker James Lorena", avatarRadius, width, targetContext)
|
||||
fragment.addAvatar("Baker James Lorena", avatarRadius, width, targetContext)
|
||||
fragment.addAvatar("email@nextcloud.localhost", avatarRadius, width, targetContext)
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showAvatarsWithStatus() {
|
||||
val avatarRadius = targetContext.resources.getDimension(R.dimen.list_item_avatar_icon_radius)
|
||||
val width = DisplayUtils.convertDpToPixel(2 * avatarRadius, targetContext)
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val fragment = AvatarTestFragment()
|
||||
|
||||
val paulette = BitmapFactory.decodeFile(getFile("paulette.jpg").absolutePath)
|
||||
val christine = BitmapFactory.decodeFile(getFile("christine.jpg").absolutePath)
|
||||
val textBitmap = BitmapUtils.drawableToBitmap(TextDrawable.createNamedAvatar("Admin", avatarRadius))
|
||||
|
||||
sut.addFragment(fragment)
|
||||
|
||||
runOnUiThread {
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(paulette, StatusType.ONLINE, "😘", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "☁️", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "🌴️", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.ONLINE, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(paulette, StatusType.DND, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(christine, StatusType.AWAY, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(paulette, StatusType.OFFLINE, "", targetContext),
|
||||
width * 2,
|
||||
1,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "😘", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "☁️", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "🌴️", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.ONLINE, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.DND, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.AWAY, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
|
||||
fragment.addBitmap(
|
||||
BitmapUtils.createAvatarWithStatus(textBitmap, StatusType.OFFLINE, "", targetContext),
|
||||
width,
|
||||
2,
|
||||
targetContext
|
||||
)
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.ui.TextDrawable
|
||||
|
||||
internal class AvatarTestFragment : Fragment() {
|
||||
lateinit var list1: LinearLayout
|
||||
lateinit var list2: LinearLayout
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view: View = inflater.inflate(R.layout.avatar_fragment, null)
|
||||
|
||||
list1 = view.findViewById(R.id.avatar_list1)
|
||||
list2 = view.findViewById(R.id.avatar_list2)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
fun addAvatar(name: String, avatarRadius: Float, width: Int, targetContext: Context) {
|
||||
val margin = padding
|
||||
val imageView = ImageView(targetContext)
|
||||
imageView.setImageDrawable(TextDrawable.createNamedAvatar(name, avatarRadius))
|
||||
|
||||
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(width, width)
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
|
||||
layoutParams.setMargins(margin, margin, margin, margin)
|
||||
imageView.layoutParams = layoutParams
|
||||
|
||||
list1.addView(imageView)
|
||||
}
|
||||
|
||||
fun addBitmap(bitmap: Bitmap, width: Int, list: Int, targetContext: Context) {
|
||||
val margin = padding
|
||||
val imageView = ImageView(targetContext)
|
||||
imageView.setImageBitmap(bitmap)
|
||||
|
||||
val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(width, width)
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
|
||||
layoutParams.setMargins(margin, margin, margin, margin)
|
||||
imageView.layoutParams = layoutParams
|
||||
|
||||
if (list == 1) {
|
||||
list1.addView(imageView)
|
||||
} else {
|
||||
list2.addView(imageView)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val padding = 10
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.Manifest
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.ui.activity.ContactsPreferenceActivity
|
||||
import com.owncloud.android.ui.fragment.contactsbackup.BackupListFragment
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class BackupListFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(ContactsPreferenceActivity::class.java, true, false)
|
||||
|
||||
@get:Rule
|
||||
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_CALENDAR)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showLoading() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val file = OCFile("/")
|
||||
val transaction = sut.supportFragmentManager.beginTransaction()
|
||||
|
||||
transaction.replace(R.id.frame_container, BackupListFragment.newInstance(file, user))
|
||||
transaction.commit()
|
||||
|
||||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showContactList() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val transaction = sut.supportFragmentManager.beginTransaction()
|
||||
val file = getFile("vcard.vcf")
|
||||
val ocFile = OCFile("/vcard.vcf")
|
||||
ocFile.storagePath = file.absolutePath
|
||||
ocFile.mimeType = "text/vcard"
|
||||
|
||||
transaction.replace(R.id.frame_container, BackupListFragment.newInstance(ocFile, user))
|
||||
transaction.commit()
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showCalendarList() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val transaction = sut.supportFragmentManager.beginTransaction()
|
||||
val file = getFile("calendar.ics")
|
||||
val ocFile = OCFile("/Private calender_2020-09-01_10-45-20.ics.ics")
|
||||
ocFile.storagePath = file.absolutePath
|
||||
ocFile.mimeType = "text/calendar"
|
||||
|
||||
transaction.replace(R.id.frame_container, BackupListFragment.newInstance(ocFile, user))
|
||||
transaction.commit()
|
||||
|
||||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showCalendarAndContactsList() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val transaction = sut.supportFragmentManager.beginTransaction()
|
||||
|
||||
val calendarFile = getFile("calendar.ics")
|
||||
val calendarOcFile = OCFile("/Private calender_2020-09-01_10-45-20.ics")
|
||||
calendarOcFile.storagePath = calendarFile.absolutePath
|
||||
calendarOcFile.mimeType = "text/calendar"
|
||||
|
||||
val contactFile = getFile("vcard.vcf")
|
||||
val contactOcFile = OCFile("/vcard.vcf")
|
||||
contactOcFile.storagePath = contactFile.absolutePath
|
||||
contactOcFile.mimeType = "text/vcard"
|
||||
|
||||
val files = arrayOf(calendarOcFile, contactOcFile)
|
||||
transaction.replace(R.id.frame_container, BackupListFragment.newInstance(files, user))
|
||||
transaction.commit()
|
||||
|
||||
waitForIdleSync()
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.nextcloud.ui.ImageDetailFragment
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.resources.activities.model.Activity
|
||||
import com.owncloud.android.lib.resources.activities.model.RichElement
|
||||
import com.owncloud.android.lib.resources.activities.model.RichObject
|
||||
import com.owncloud.android.lib.resources.activities.models.PreviewObject
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.GregorianCalendar
|
||||
|
||||
class FileDetailFragmentStaticServerIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
var file = getFile("gps.jpg")
|
||||
val oCFile = OCFile("/").apply {
|
||||
storagePath = file.absolutePath
|
||||
fileId = 12
|
||||
fileDataStorageManager.saveFile(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showFileDetailActivitiesFragment() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
sut.addFragment(FileDetailActivitiesFragment.newInstance(oCFile, user))
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showFileDetailSharingFragment() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
sut.addFragment(FileDetailSharingFragment.newInstance(oCFile, user))
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showFileDetailDetailsFragment() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
val sut = ImageDetailFragment.newInstance(oCFile, user)
|
||||
activity.addFragment(sut)
|
||||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.hideMap()
|
||||
}
|
||||
|
||||
screenshot(activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
@Suppress("MagicNumber")
|
||||
fun showDetailsActivities() {
|
||||
val date = GregorianCalendar()
|
||||
date.set(2005, 4, 17, 10, 35, 30) // random date
|
||||
|
||||
val richObjectList: ArrayList<RichObject> = ArrayList()
|
||||
richObjectList.add(RichObject("file", "abc", "text.txt", "/text.txt", "link", "tag"))
|
||||
richObjectList.add(RichObject("file", "1", "text.txt", "/text.txt", "link", "tag"))
|
||||
|
||||
val previewObjectList1: ArrayList<PreviewObject> = ArrayList()
|
||||
previewObjectList1.add(PreviewObject(1, "source", "link", true, "text/plain", "view", "text.txt"))
|
||||
|
||||
val richObjectList2: ArrayList<RichObject> = ArrayList()
|
||||
richObjectList2.add(RichObject("user", "admin", "Admin", "", "", ""))
|
||||
|
||||
val activities = mutableListOf(
|
||||
Activity(
|
||||
1,
|
||||
date.time,
|
||||
date.time,
|
||||
"files",
|
||||
"file_changed",
|
||||
"user1",
|
||||
"user1",
|
||||
"You changed text.txt",
|
||||
"",
|
||||
"icon",
|
||||
"link",
|
||||
"files",
|
||||
"1",
|
||||
"/text.txt",
|
||||
previewObjectList1,
|
||||
RichElement("", richObjectList)
|
||||
),
|
||||
Activity(
|
||||
2,
|
||||
date.time,
|
||||
date.time,
|
||||
"comments",
|
||||
"comments",
|
||||
"user1",
|
||||
"user1",
|
||||
"admin commented",
|
||||
"test2",
|
||||
"icon",
|
||||
"link",
|
||||
"files",
|
||||
"1",
|
||||
"/text.txt",
|
||||
emptyList(),
|
||||
RichElement()
|
||||
)
|
||||
)
|
||||
|
||||
val sut = FileDetailFragment.newInstance(oCFile, user, 0)
|
||||
testActivityRule.launchActivity(null).apply {
|
||||
addFragment(sut)
|
||||
waitForIdleSync()
|
||||
runOnUiThread {
|
||||
sut.fileDetailActivitiesFragment.populateList(activities as List<Any>?, true)
|
||||
}
|
||||
longSleep()
|
||||
screenshot(sut.fileDetailActivitiesFragment.binding.swipeContainingList)
|
||||
}
|
||||
}
|
||||
|
||||
// @Test
|
||||
// @ScreenshotTest
|
||||
fun showDetailsActivitiesNone() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
val sut = FileDetailFragment.newInstance(oCFile, user, 0)
|
||||
activity.addFragment(sut)
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.fileDetailActivitiesFragment.populateList(emptyList(), true)
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(sut.fileDetailActivitiesFragment.binding.list)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showDetailsActivitiesError() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
val sut = FileDetailFragment.newInstance(oCFile, user, 0)
|
||||
activity.addFragment(sut)
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.fileDetailActivitiesFragment.disableLoadingActivities()
|
||||
sut
|
||||
.fileDetailActivitiesFragment
|
||||
.setErrorContent(targetContext.resources.getString(R.string.file_detail_activity_error))
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(sut.fileDetailActivitiesFragment.binding.emptyList.emptyListView)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showDetailsSharing() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
sut.addFragment(FileDetailFragment.newInstance(oCFile, user, 1))
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,830 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2021 TSI-mc
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.view.View
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.accessibility.AccessibilityChecks
|
||||
import androidx.test.espresso.action.ViewActions
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isChecked
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultBaseUtils.matchesCheckNames
|
||||
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.nextcloud.test.RetryTestRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.resources.shares.OCShare
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.CREATE_PERMISSION_FLAG
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.DELETE_PERMISSION_FLAG
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.NO_PERMISSION
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.READ_PERMISSION_FLAG
|
||||
import com.owncloud.android.lib.resources.shares.OCShare.Companion.SHARE_PERMISSION_FLAG
|
||||
import com.owncloud.android.lib.resources.shares.ShareType
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity
|
||||
import com.owncloud.android.ui.fragment.util.SharingMenuHelper
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.hamcrest.CoreMatchers.allOf
|
||||
import org.hamcrest.CoreMatchers.anyOf
|
||||
import org.hamcrest.CoreMatchers.`is`
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class FileDetailSharingFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@get:Rule
|
||||
val retryRule = RetryTestRule()
|
||||
|
||||
lateinit var file: OCFile
|
||||
lateinit var folder: OCFile
|
||||
lateinit var activity: TestActivity
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
activity = testActivityRule.launchActivity(null)
|
||||
file = OCFile("/test.md").apply {
|
||||
remoteId = "00000001"
|
||||
parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
fileDataStorageManager.saveFile(this)
|
||||
}
|
||||
|
||||
folder = OCFile("/test").apply {
|
||||
setFolder()
|
||||
parentId = activity.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun listSharesFileNone() {
|
||||
show(file)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun listSharesFileResharingNotAllowed() {
|
||||
file.permissions = ""
|
||||
|
||||
show(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use same values as {@link OCFileListFragmentStaticServerIT showSharedFiles }
|
||||
*/
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
@Suppress("MagicNumber")
|
||||
fun listSharesFileAllShareTypes() {
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 1
|
||||
shareType = ShareType.USER
|
||||
sharedWithDisplayName = "Admin"
|
||||
permissions = MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 2
|
||||
shareType = ShareType.GROUP
|
||||
sharedWithDisplayName = "Group"
|
||||
permissions = MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 3
|
||||
shareType = ShareType.EMAIL
|
||||
sharedWithDisplayName = "admin@nextcloud.localhost"
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 4
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
label = "Customer"
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 5
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
label = "Colleagues"
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 6
|
||||
shareType = ShareType.FEDERATED
|
||||
sharedWithDisplayName = "admin@nextcloud.localhost"
|
||||
permissions = OCShare.FEDERATED_PERMISSIONS_FOR_FILE
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 7
|
||||
shareType = ShareType.CIRCLE
|
||||
sharedWithDisplayName = "Personal team"
|
||||
permissions = SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 8
|
||||
shareType = ShareType.CIRCLE
|
||||
sharedWithDisplayName = "Public team"
|
||||
permissions = SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 9
|
||||
shareType = ShareType.CIRCLE
|
||||
sharedWithDisplayName = "Closed team"
|
||||
permissions = SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 10
|
||||
shareType = ShareType.CIRCLE
|
||||
sharedWithDisplayName = "Secret team"
|
||||
permissions = SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 11
|
||||
shareType = ShareType.ROOM
|
||||
sharedWithDisplayName = "Admin"
|
||||
permissions = SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 12
|
||||
shareType = ShareType.ROOM
|
||||
sharedWithDisplayName = "Meeting"
|
||||
permissions = SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
activity.storageManager.saveShare(this)
|
||||
}
|
||||
|
||||
show(file)
|
||||
}
|
||||
|
||||
private fun show(file: OCFile) {
|
||||
val fragment = FileDetailSharingFragment.newInstance(file, user)
|
||||
|
||||
activity.addFragment(fragment)
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(activity)
|
||||
}
|
||||
|
||||
// public link and email are handled the same way
|
||||
// for advanced permissions
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun publicLinkOptionMenuFolderAdvancePermission() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val publicShare = OCShare().apply {
|
||||
isFolder = true
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
activity.runOnUiThread { sut.showSharingMenuActionSheet(publicShare) }
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
// check if items are visible
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed()))
|
||||
|
||||
// click event
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
|
||||
|
||||
// validate view shown on screen
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(not(isDisplayed())))
|
||||
|
||||
// read-only
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// upload and editing
|
||||
publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// file drop
|
||||
publicShare.permissions = 4
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
// password protection
|
||||
publicShare.shareWith = "someValue"
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
publicShare.shareWith = ""
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// hide download
|
||||
publicShare.isHideFileDownload = true
|
||||
publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
publicShare.isHideFileDownload = false
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
publicShare.expirationDate = 1582019340000
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
|
||||
goBack()
|
||||
|
||||
publicShare.expirationDate = 0
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
|
||||
}
|
||||
|
||||
// public link and email are handled the same way
|
||||
// for send new email
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun publicLinkOptionMenuFolderSendNewEmail() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val publicShare = OCShare().apply {
|
||||
isFolder = true
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
verifySendNewEmail(sut, publicShare)
|
||||
}
|
||||
|
||||
private fun setupSecondaryFragment() {
|
||||
val parentFolder = OCFile("/")
|
||||
val secondary = FileDetailFragment.newInstance(file, parentFolder, user)
|
||||
activity.addSecondaryFragment(secondary, FileDisplayActivity.TAG_LIST_OF_FILES)
|
||||
activity.addView(
|
||||
FloatingActionButton(activity).apply {
|
||||
// needed for some reason
|
||||
visibility = View.GONE
|
||||
id = R.id.fab_main
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// public link and email are handled the same way
|
||||
// for advanced permissions
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun publicLinkOptionMenuFileAdvancePermission() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val publicShare = OCShare().apply {
|
||||
isFolder = false
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
permissions = 17
|
||||
}
|
||||
activity.handler.post { sut.showSharingMenuActionSheet(publicShare) }
|
||||
waitForIdleSync()
|
||||
|
||||
// check if items are visible
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(isDisplayed()))
|
||||
|
||||
// click event
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
|
||||
|
||||
// validate view shown on screen
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(not(isDisplayed())))
|
||||
|
||||
// read-only
|
||||
publicShare.permissions = 17 // from server
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// editing
|
||||
publicShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
// hide download
|
||||
publicShare.isHideFileDownload = true
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
publicShare.isHideFileDownload = false
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// password protection
|
||||
publicShare.isPasswordProtected = true
|
||||
publicShare.shareWith = "someValue"
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
publicShare.isPasswordProtected = false
|
||||
publicShare.shareWith = ""
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// expires
|
||||
publicShare.expirationDate = 1582019340
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
|
||||
goBack()
|
||||
|
||||
publicShare.expirationDate = 0
|
||||
openAdvancedPermissions(sut, publicShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
|
||||
}
|
||||
|
||||
// public link and email are handled the same way
|
||||
// for send new email
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun publicLinkOptionMenuFileSendNewEmail() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val publicShare = OCShare().apply {
|
||||
isFolder = false
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
verifySendNewEmail(sut, publicShare)
|
||||
}
|
||||
|
||||
// also applies for
|
||||
// group
|
||||
// conversation
|
||||
// circle
|
||||
// federated share
|
||||
// for advanced permissions
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun userOptionMenuFileAdvancePermission() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
suppressFDFAccessibilityChecks()
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val userShare = OCShare().apply {
|
||||
isFolder = false
|
||||
shareType = ShareType.USER
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
// check if items are visible
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed())))
|
||||
|
||||
// click event
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
// validate view shown on screen
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isDisplayed()))
|
||||
|
||||
// read-only
|
||||
userShare.permissions = 17 // from server
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// editing
|
||||
userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FILE // from server
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
// allow reshare
|
||||
userShare.permissions = 1 // from server
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
userShare.permissions = 17 // from server
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
// set expiration date
|
||||
userShare.expirationDate = 1582019340000
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
|
||||
goBack()
|
||||
|
||||
userShare.expirationDate = 0
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
|
||||
}
|
||||
|
||||
private fun suppressFDFAccessibilityChecks() {
|
||||
AccessibilityChecks.enable().apply {
|
||||
setSuppressingResultMatcher(
|
||||
allOf(
|
||||
anyOf(
|
||||
matchesCheckNames(`is`("TouchTargetSizeCheck")),
|
||||
matchesCheckNames(`is`("SpeakableTextPresentCheck"))
|
||||
),
|
||||
anyOf(
|
||||
matchesViews(ViewMatchers.withId(R.id.favorite)),
|
||||
matchesViews(ViewMatchers.withId(R.id.last_modification_timestamp))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// also applies for
|
||||
// group
|
||||
// conversation
|
||||
// circle
|
||||
// federated share
|
||||
// for send new email
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun userOptionMenuFileSendNewEmail() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val userShare = OCShare().apply {
|
||||
isFolder = false
|
||||
shareType = ShareType.USER
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
verifySendNewEmail(sut, userShare)
|
||||
}
|
||||
|
||||
// also applies for
|
||||
// group
|
||||
// conversation
|
||||
// circle
|
||||
// federated share
|
||||
// for advanced permissions
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun userOptionMenuFolderAdvancePermission() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
suppressFDFAccessibilityChecks()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val userShare = OCShare().apply {
|
||||
isFolder = true
|
||||
shareType = ShareType.USER
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
// check if items are visible
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_link)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_unshare)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.menu_share_add_another_link)).check(matches(not(isDisplayed())))
|
||||
|
||||
// click event
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
|
||||
|
||||
// validate view shown on screen
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isDisplayed()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_hide_download_checkbox)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_password_switch)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_change_name_switch)).check(matches(not(isDisplayed())))
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isDisplayed()))
|
||||
|
||||
// read-only
|
||||
userShare.permissions = 17 // from server
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// allow upload & editing
|
||||
userShare.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER // from server
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
// file drop
|
||||
userShare.permissions = 4
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_read_only)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_upload_editing)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_permission_file_drop)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
// allow reshare
|
||||
userShare.permissions = 1 // from server
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isNotChecked()))
|
||||
goBack()
|
||||
|
||||
userShare.permissions = 17 // from server
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_allow_resharing_checkbox)).check(matches(isChecked()))
|
||||
goBack()
|
||||
|
||||
// set expiration date
|
||||
userShare.expirationDate = 1582019340000
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(not(withText(""))))
|
||||
goBack()
|
||||
|
||||
userShare.expirationDate = 0
|
||||
openAdvancedPermissions(sut, userShare)
|
||||
onView(ViewMatchers.withId(R.id.share_process_set_exp_date_switch)).check(matches(isNotChecked()))
|
||||
onView(ViewMatchers.withId(R.id.share_process_select_exp_date)).check(matches(withText("")))
|
||||
}
|
||||
|
||||
// open bottom sheet with actions
|
||||
private fun openAdvancedPermissions(
|
||||
sut: FileDetailSharingFragment,
|
||||
userShare: OCShare
|
||||
) {
|
||||
activity.handler.post {
|
||||
sut.showSharingMenuActionSheet(userShare)
|
||||
}
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
onView(ViewMatchers.withId(R.id.menu_share_advanced_permissions)).perform(ViewActions.click())
|
||||
}
|
||||
|
||||
// remove the fragment shown
|
||||
private fun goBack() {
|
||||
activity.handler.post {
|
||||
val processFragment =
|
||||
activity.supportFragmentManager.findFragmentByTag(FileDetailsSharingProcessFragment.TAG) as
|
||||
FileDetailsSharingProcessFragment
|
||||
processFragment.onBackPressed()
|
||||
}
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
}
|
||||
|
||||
// also applies for
|
||||
// group
|
||||
// conversation
|
||||
// circle
|
||||
// federated share
|
||||
// for send new email
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun userOptionMenuFolderSendNewEmail() {
|
||||
val sut = FileDetailSharingFragment.newInstance(file, user)
|
||||
activity.addFragment(sut)
|
||||
setupSecondaryFragment()
|
||||
shortSleep()
|
||||
sut.refreshCapabilitiesFromDB()
|
||||
|
||||
val userShare = OCShare().apply {
|
||||
isFolder = true
|
||||
shareType = ShareType.USER
|
||||
permissions = 17
|
||||
}
|
||||
|
||||
verifySendNewEmail(sut, userShare)
|
||||
}
|
||||
|
||||
/**
|
||||
* verify send new email note text
|
||||
*/
|
||||
private fun verifySendNewEmail(
|
||||
sut: FileDetailSharingFragment,
|
||||
userShare: OCShare
|
||||
) {
|
||||
activity.runOnUiThread { sut.showSharingMenuActionSheet(userShare) }
|
||||
|
||||
waitForIdleSync()
|
||||
// click event
|
||||
onView(ViewMatchers.withId(R.id.menu_share_send_new_email)).perform(ViewActions.click())
|
||||
|
||||
// validate view shown on screen
|
||||
onView(ViewMatchers.withId(R.id.note_text)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUploadAndEditingSharePermissions() {
|
||||
val share = OCShare().apply {
|
||||
permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
}
|
||||
assertTrue(SharingMenuHelper.isUploadAndEditingAllowed(share))
|
||||
|
||||
share.permissions = NO_PERMISSION
|
||||
assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
|
||||
|
||||
share.permissions = READ_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
|
||||
|
||||
share.permissions = CREATE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
|
||||
|
||||
share.permissions = DELETE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
|
||||
|
||||
share.permissions = SHARE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isUploadAndEditingAllowed(share))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun testReadOnlySharePermissions() {
|
||||
val share = OCShare().apply {
|
||||
permissions = 17
|
||||
}
|
||||
assertTrue(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = NO_PERMISSION
|
||||
assertFalse(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = READ_PERMISSION_FLAG
|
||||
assertTrue(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = CREATE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = DELETE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = SHARE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
assertFalse(SharingMenuHelper.isReadOnly(share))
|
||||
|
||||
share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
assertFalse(SharingMenuHelper.isReadOnly(share))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun testFileDropSharePermissions() {
|
||||
val share = OCShare().apply {
|
||||
permissions = 4
|
||||
}
|
||||
assertTrue(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = NO_PERMISSION
|
||||
assertFalse(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = READ_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = CREATE_PERMISSION_FLAG
|
||||
assertTrue(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = DELETE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = SHARE_PERMISSION_FLAG
|
||||
assertFalse(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = MAXIMUM_PERMISSIONS_FOR_FOLDER
|
||||
assertFalse(SharingMenuHelper.isFileDrop(share))
|
||||
|
||||
share.permissions = MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
assertFalse(SharingMenuHelper.isFileDrop(share))
|
||||
}
|
||||
|
||||
@After
|
||||
override fun after() {
|
||||
activity.storageManager.cleanShares()
|
||||
activity.finish()
|
||||
|
||||
super.after()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager.InitDiskCacheTask
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE
|
||||
import com.owncloud.android.lib.common.utils.Log_OC
|
||||
import com.owncloud.android.lib.resources.files.model.ImageDimension
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.Random
|
||||
|
||||
class GalleryFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
lateinit var activity: TestActivity
|
||||
val random = Random(1)
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
activity = testActivityRule.launchActivity(null)
|
||||
|
||||
// initialise thumbnails cache on background thread
|
||||
InitDiskCacheTask().execute()
|
||||
}
|
||||
|
||||
@After
|
||||
override fun after() {
|
||||
ThumbnailsCacheManager.clearCache()
|
||||
|
||||
super.after()
|
||||
}
|
||||
|
||||
@ScreenshotTest
|
||||
@Test
|
||||
fun showEmpty() {
|
||||
val sut = GalleryFragment()
|
||||
activity.addFragment(sut)
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showGallery() {
|
||||
createImage(10000001, 700, 300)
|
||||
createImage(10000002, 500, 300)
|
||||
createImage(10000007, 300, 400)
|
||||
|
||||
val sut = GalleryFragment()
|
||||
activity.addFragment(sut)
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
screenshot(activity)
|
||||
}
|
||||
|
||||
private fun createImage(id: Int, width: Int? = null, height: Int? = null) {
|
||||
val defaultSize = ThumbnailsCacheManager.getThumbnailDimension().toFloat()
|
||||
val file = OCFile("/$id.png").apply {
|
||||
fileId = id.toLong()
|
||||
remoteId = "$id"
|
||||
mimeType = "image/png"
|
||||
isPreviewAvailable = true
|
||||
modificationTimestamp = (1658475504 + id.toLong()) * 1000
|
||||
imageDimension = ImageDimension(width?.toFloat() ?: defaultSize, height?.toFloat() ?: defaultSize)
|
||||
storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
// create dummy thumbnail
|
||||
val w: Int
|
||||
val h: Int
|
||||
if (width == null || height == null) {
|
||||
if (random.nextBoolean()) {
|
||||
// portrait
|
||||
w = (random.nextInt(3) + 2) * 100 // 200-400
|
||||
h = (random.nextInt(5) + 4) * 100 // 400-800
|
||||
} else {
|
||||
// landscape
|
||||
w = (random.nextInt(5) + 4) * 100 // 400-800
|
||||
h = (random.nextInt(3) + 2) * 100 // 200-400
|
||||
}
|
||||
} else {
|
||||
w = width
|
||||
h = height
|
||||
}
|
||||
|
||||
val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
||||
Canvas(bitmap).apply {
|
||||
drawRGB(random.nextInt(256), random.nextInt(256), random.nextInt(256))
|
||||
drawCircle(w / 2f, h / 2f, w.coerceAtMost(h) / 2f, Paint().apply { color = Color.BLACK })
|
||||
}
|
||||
ThumbnailsCacheManager.addBitmapToCache(PREFIX_RESIZED_IMAGE + file.remoteId, bitmap)
|
||||
|
||||
assertNotNull(ThumbnailsCacheManager.getBitmapFromDiskCache(PREFIX_RESIZED_IMAGE + file.remoteId))
|
||||
|
||||
Log_OC.d("Gallery_thumbnail", "created $id with ${bitmap.width} x ${bitmap.height}")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.android.lib.resources.groupfolders.Groupfolder
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class GroupfolderListFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
lateinit var activity: TestActivity
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
activity = testActivityRule.launchActivity(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showGroupfolder() {
|
||||
val sut = GroupfolderListFragment()
|
||||
activity.addFragment(sut)
|
||||
|
||||
shortSleep() // to let async task finish
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.setAdapter(null)
|
||||
sut.setData(
|
||||
mapOf(
|
||||
Pair("2", Groupfolder(2, "/subfolder/group"))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
screenshot(activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showGroupfolders() {
|
||||
val sut = GroupfolderListFragment()
|
||||
activity.addFragment(sut)
|
||||
|
||||
shortSleep() // to let async task finish
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.setAdapter(null)
|
||||
sut.setData(
|
||||
mapOf(
|
||||
Pair("1", Groupfolder(1, "/test/")),
|
||||
Pair("2", Groupfolder(2, "/subfolder/group"))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
screenshot(activity)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.GrantStoragePermissionRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.resources.shares.ShareType
|
||||
import com.owncloud.android.lib.resources.shares.ShareeUser
|
||||
import com.owncloud.android.utils.MimeType
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Assert
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class OCFileListFragmentStaticServerIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@get:Rule
|
||||
val permissionRule = GrantStoragePermissionRule.grant()
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
@Suppress("MagicNumber")
|
||||
fun showFiles() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
|
||||
OCFile("/1.png").apply {
|
||||
mimeType = "image/png"
|
||||
fileLength = 1024000
|
||||
modificationTimestamp = 1188206955000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/image.png").apply {
|
||||
mimeType = "image/png"
|
||||
isPreviewAvailable = false
|
||||
fileLength = 3072000
|
||||
modificationTimestamp = 746443755000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
tags = listOf("Top secret")
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/live photo.png").apply {
|
||||
mimeType = "image/png"
|
||||
isPreviewAvailable = false
|
||||
fileLength = 3072000
|
||||
modificationTimestamp = 746443755000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
setLivePhoto("/video.mov")
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/video.mp4").apply {
|
||||
mimeType = "video/mp4"
|
||||
isPreviewAvailable = false
|
||||
fileLength = 12092000
|
||||
modificationTimestamp = 746143952000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
tags = listOf("Confidential", "+5")
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
sut.addFragment(OCFileListFragment())
|
||||
|
||||
val fragment = (sut.fragment as OCFileListFragment)
|
||||
val root = sut.storageManager.getFileByEncryptedRemotePath("/")
|
||||
|
||||
shortSleep()
|
||||
|
||||
sut.runOnUiThread { fragment.listDirectory(root, false, false) }
|
||||
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use same values as {@link FileDetailSharingFragmentIT listSharesFileAllShareTypes }
|
||||
*/
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showSharedFiles() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val fragment = OCFileListFragment()
|
||||
|
||||
OCFile("/sharedToUser.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(ShareeUser("Admin", "Server Admin", ShareType.USER))
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedToGroup.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(ShareeUser("group", "Group", ShareType.GROUP))
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedToEmail.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(ShareeUser("admin@nextcloud.localhost", "admin@nextcloud.localhost", ShareType.EMAIL))
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/publicLink.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedViaLink = true
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedToFederatedUser.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(
|
||||
ShareeUser("admin@remote.nextcloud.com", "admin@remote.nextcloud.com (remote)", ShareType.FEDERATED)
|
||||
)
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedToPersonalCircle.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(ShareeUser("circle", "Circle (Personal circle)", ShareType.CIRCLE))
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
// as we cannot distinguish circle types, we do not need them right now
|
||||
// OCFile("/sharedToPublicCircle.jpg").apply {
|
||||
// parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
// isSharedWithSharee = true
|
||||
// sharees = listOf(ShareeUser("circle", "Circle (Public circle)", ShareType.CIRCLE))
|
||||
// modificationTimestamp = 1000
|
||||
// sut.storageManager.saveFile(this)
|
||||
// }
|
||||
//
|
||||
// OCFile("/sharedToClosedCircle.jpg").apply {
|
||||
// parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
// isSharedWithSharee = true
|
||||
// sharees = listOf(ShareeUser("circle", "Circle (Closed circle)", ShareType.CIRCLE))
|
||||
// modificationTimestamp = 1000
|
||||
// sut.storageManager.saveFile(this)
|
||||
// }
|
||||
//
|
||||
// OCFile("/sharedToSecretCircle.jpg").apply {
|
||||
// parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
// isSharedWithSharee = true
|
||||
// sharees = listOf(ShareeUser("circle", "Circle (Secret circle)", ShareType.CIRCLE))
|
||||
// modificationTimestamp = 1000
|
||||
// sut.storageManager.saveFile(this)
|
||||
// }
|
||||
|
||||
OCFile("/sharedToUserRoom.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(ShareeUser("Conversation", "Admin", ShareType.ROOM))
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedToGroupRoom.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(ShareeUser("Conversation", "Meeting", ShareType.ROOM))
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedToUsers.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
isSharedWithSharee = true
|
||||
sharees = listOf(
|
||||
ShareeUser("Admin", "Server Admin", ShareType.USER),
|
||||
ShareeUser("User", "User", ShareType.USER),
|
||||
ShareeUser("Christine", "Christine Scott", ShareType.USER)
|
||||
)
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/notShared.jpg").apply {
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
modificationTimestamp = 1000
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
sut.addFragment(fragment)
|
||||
|
||||
shortSleep()
|
||||
|
||||
val root = sut.storageManager.getFileByEncryptedRemotePath("/")
|
||||
|
||||
sut.runOnUiThread {
|
||||
fragment.listDirectory(root, false, false)
|
||||
fragment.adapter.setShowShareAvatar(true)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use same values as {@link FileDetailSharingFragmentIT listSharesFileAllShareTypes }
|
||||
*/
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showFolderTypes() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val fragment = OCFileListFragment()
|
||||
|
||||
OCFile("/normal/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
modificationTimestamp = 1624003571000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/sharedViaLink/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
isSharedViaLink = true
|
||||
modificationTimestamp = 1619003571000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/share/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
isSharedWithSharee = true
|
||||
modificationTimestamp = 1619303571000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/groupFolder/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
modificationTimestamp = 1615003571000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
permissions += "M"
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/encrypted/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
isEncrypted = true
|
||||
decryptedRemotePath = "/encrypted/"
|
||||
modificationTimestamp = 1614003571000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
OCFile("/locked/").apply {
|
||||
mimeType = MimeType.DIRECTORY
|
||||
isLocked = true
|
||||
decryptedRemotePath = "/locked/"
|
||||
modificationTimestamp = 1613003571000
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
sut.addFragment(fragment)
|
||||
|
||||
shortSleep()
|
||||
|
||||
val root = sut.storageManager.getFileByEncryptedRemotePath("/")
|
||||
|
||||
sut.runOnUiThread {
|
||||
fragment.listDirectory(root, false, false)
|
||||
fragment.adapter.setShowShareAvatar(true)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
@Suppress("MagicNumber")
|
||||
fun showRichWorkspace() {
|
||||
val sut = testActivityRule.launchActivity(null)
|
||||
val fragment = OCFileListFragment()
|
||||
|
||||
val folder = OCFile("/test/")
|
||||
folder.setFolder()
|
||||
sut.storageManager.saveFile(folder)
|
||||
|
||||
val imageFile = OCFile("/test/image.png")
|
||||
imageFile.mimeType = "image/png"
|
||||
imageFile.fileLength = 1024000
|
||||
imageFile.modificationTimestamp = 1188206955000
|
||||
imageFile.parentId = sut.storageManager.getFileByEncryptedRemotePath("/test/").fileId
|
||||
imageFile.storagePath = getFile("java.md").absolutePath
|
||||
sut.storageManager.saveFile(imageFile)
|
||||
|
||||
sut.addFragment(fragment)
|
||||
val testFolder: OCFile = sut.storageManager.getFileByEncryptedRemotePath("/test/")
|
||||
testFolder.richWorkspace = getFile("java.md").readText()
|
||||
|
||||
sut.runOnUiThread { fragment.listDirectory(testFolder, false, false) }
|
||||
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldShowHeader() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
val sut = OCFileListFragment()
|
||||
|
||||
val folder = OCFile("/test/")
|
||||
folder.setFolder()
|
||||
activity.storageManager.saveFile(folder)
|
||||
|
||||
activity.addFragment(sut)
|
||||
val testFolder: OCFile = activity.storageManager.getFileByEncryptedRemotePath("/test/")
|
||||
|
||||
activity.runOnUiThread {
|
||||
// richWorkspace is not set
|
||||
Assert.assertFalse(sut.adapter.shouldShowHeader())
|
||||
|
||||
testFolder.richWorkspace = " "
|
||||
activity.storageManager.saveFile(testFolder)
|
||||
sut.adapter.swapDirectory(user, testFolder, activity.storageManager, false, "")
|
||||
Assert.assertFalse(sut.adapter.shouldShowHeader())
|
||||
|
||||
testFolder.richWorkspace = null
|
||||
activity.storageManager.saveFile(testFolder)
|
||||
sut.adapter.swapDirectory(user, testFolder, activity.storageManager, false, "")
|
||||
Assert.assertFalse(sut.adapter.shouldShowHeader())
|
||||
|
||||
testFolder.richWorkspace = "1"
|
||||
activity.storageManager.saveFile(testFolder)
|
||||
sut.adapter.setCurrentDirectory(testFolder)
|
||||
Assert.assertTrue(sut.adapter.shouldShowHeader())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import android.view.View
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.GrantStoragePermissionRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.resources.shares.OCShare
|
||||
import com.owncloud.android.lib.resources.shares.ShareType
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
internal class SharedListFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@get:Rule
|
||||
val permissionRule = GrantStoragePermissionRule.grant()
|
||||
|
||||
lateinit var sut: TestActivity
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
sut = testActivityRule.launchActivity(null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showSharedFiles() {
|
||||
val fragment = SharedListFragment()
|
||||
|
||||
val file = OCFile("/shared to admin.png").apply {
|
||||
remoteId = "00000001"
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
mimeType = "image/png"
|
||||
fileLength = 1024000
|
||||
modificationTimestamp = 1188206955
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
val file1 = OCFile("/shared to group.png").apply {
|
||||
remoteId = "00000001"
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
mimeType = "image/png"
|
||||
fileLength = 1024000
|
||||
modificationTimestamp = 1188206955
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
val file2 = OCFile("/shared via public link.png").apply {
|
||||
remoteId = "00000001"
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
mimeType = "image/png"
|
||||
fileLength = 1024000
|
||||
modificationTimestamp = 1188206955
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
val file3 = OCFile("/shared to personal circle.png").apply {
|
||||
remoteId = "00000001"
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
mimeType = "image/png"
|
||||
fileLength = 1024000
|
||||
modificationTimestamp = 1188206955
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
val file4 = OCFile("/shared to talk.png").apply {
|
||||
remoteId = "00000001"
|
||||
parentId = sut.storageManager.getFileByEncryptedRemotePath("/").fileId
|
||||
mimeType = "image/png"
|
||||
fileLength = 1024000
|
||||
modificationTimestamp = 1188206955
|
||||
permissions = OCFile.PERMISSION_CAN_RESHARE
|
||||
sut.storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
val shares = listOf(
|
||||
OCShare(file.decryptedRemotePath).apply {
|
||||
remoteId = 1
|
||||
shareType = ShareType.USER
|
||||
sharedWithDisplayName = "Admin"
|
||||
permissions = OCShare.MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
userId = getUserId(user)
|
||||
sharedDate = 1188206955
|
||||
mimetype = "image/png"
|
||||
sut.storageManager.saveShare(this)
|
||||
},
|
||||
|
||||
OCShare(file1.decryptedRemotePath).apply {
|
||||
remoteId = 2
|
||||
shareType = ShareType.GROUP
|
||||
sharedWithDisplayName = "Group"
|
||||
permissions = OCShare.MAXIMUM_PERMISSIONS_FOR_FILE
|
||||
userId = getUserId(user)
|
||||
sharedDate = 1188206955
|
||||
mimetype = "image/png"
|
||||
sut.storageManager.saveShare(this)
|
||||
},
|
||||
|
||||
OCShare(file2.decryptedRemotePath).apply {
|
||||
remoteId = 3
|
||||
shareType = ShareType.PUBLIC_LINK
|
||||
label = "Customer"
|
||||
sharedDate = 1188206955
|
||||
mimetype = "image/png"
|
||||
sut.storageManager.saveShare(this)
|
||||
},
|
||||
|
||||
OCShare(file3.decryptedRemotePath).apply {
|
||||
remoteId = 4
|
||||
shareType = ShareType.CIRCLE
|
||||
sharedWithDisplayName = "Personal circle"
|
||||
permissions = OCShare.SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
sharedDate = 1188206955
|
||||
mimetype = "image/png"
|
||||
sut.storageManager.saveShare(this)
|
||||
},
|
||||
|
||||
OCShare(file4.decryptedRemotePath).apply {
|
||||
remoteId = 11
|
||||
shareType = ShareType.ROOM
|
||||
sharedWithDisplayName = "Admin"
|
||||
permissions = OCShare.SHARE_PERMISSION_FLAG
|
||||
userId = getUserId(user)
|
||||
sharedDate = 1188206955
|
||||
mimetype = "image/png"
|
||||
sut.storageManager.saveShare(this)
|
||||
}
|
||||
)
|
||||
|
||||
sut.addFragment(fragment)
|
||||
|
||||
shortSleep()
|
||||
|
||||
sut.runOnUiThread {
|
||||
fragment.isLoading = false
|
||||
fragment.mEmptyListContainer.visibility = View.GONE
|
||||
fragment.adapter.setData(
|
||||
shares,
|
||||
SearchType.SHARED_FILTER,
|
||||
storageManager,
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
waitForIdleSync()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
shortSleep()
|
||||
|
||||
screenshot(sut)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import com.owncloud.android.lib.common.SearchResult
|
||||
import com.owncloud.android.lib.common.SearchResultEntry
|
||||
import com.owncloud.android.ui.unifiedsearch.IUnifiedSearchRepository
|
||||
import com.owncloud.android.ui.unifiedsearch.ProviderID
|
||||
import com.owncloud.android.ui.unifiedsearch.UnifiedSearchResult
|
||||
|
||||
class UnifiedSearchFakeRepository : IUnifiedSearchRepository {
|
||||
|
||||
override fun queryAll(
|
||||
query: String,
|
||||
onResult: (UnifiedSearchResult) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onFinished: (Boolean) -> Unit
|
||||
) {
|
||||
val result = UnifiedSearchResult(
|
||||
provider = "files",
|
||||
success = true,
|
||||
result = SearchResult(
|
||||
"files",
|
||||
false,
|
||||
listOf(
|
||||
SearchResultEntry(
|
||||
"thumbnailUrl",
|
||||
"Test",
|
||||
"in Files",
|
||||
"http://localhost/nc/index.php/apps/files/?dir=/Files&scrollto=Test",
|
||||
"icon",
|
||||
false
|
||||
),
|
||||
SearchResultEntry(
|
||||
"thumbnailUrl",
|
||||
"Test1",
|
||||
"in Folder",
|
||||
"http://localhost/nc/index.php/apps/files/?dir=/folder&scrollto=test1.txt",
|
||||
"icon",
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
onResult(result)
|
||||
onFinished(true)
|
||||
}
|
||||
|
||||
override fun queryProvider(
|
||||
query: String,
|
||||
provider: ProviderID,
|
||||
cursor: Int?,
|
||||
onResult: (UnifiedSearchResult) -> Unit,
|
||||
onError: (Throwable) -> Unit,
|
||||
onFinished: (Boolean) -> Unit
|
||||
) {
|
||||
val result = UnifiedSearchResult(
|
||||
provider = provider,
|
||||
success = true,
|
||||
result = SearchResult(
|
||||
provider,
|
||||
false,
|
||||
listOf(
|
||||
SearchResultEntry(
|
||||
"thumbnailUrl",
|
||||
"Test",
|
||||
"in Files",
|
||||
"http://localhost/nc/index.php/apps/files/?dir=/Files&scrollto=Test",
|
||||
"icon",
|
||||
false
|
||||
),
|
||||
SearchResultEntry(
|
||||
"thumbnailUrl",
|
||||
"Test1",
|
||||
"in Folder",
|
||||
"http://localhost/nc/index.php/apps/files/?dir=/folder&scrollto=test1.txt",
|
||||
"icon",
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.fragment
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.lib.common.SearchResultEntry
|
||||
import com.owncloud.android.ui.unifiedsearch.UnifiedSearchSection
|
||||
import com.owncloud.android.ui.unifiedsearch.UnifiedSearchViewModel
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class UnifiedSearchFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
fun showSearchResult() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
val sut = UnifiedSearchFragment.newInstance(null, null)
|
||||
|
||||
activity.addFragment(sut)
|
||||
|
||||
shortSleep()
|
||||
|
||||
UiThreadStatement.runOnUiThread {
|
||||
sut.onSearchResultChanged(
|
||||
listOf(
|
||||
UnifiedSearchSection(
|
||||
providerID = "files",
|
||||
name = "Files",
|
||||
entries = listOf(
|
||||
SearchResultEntry(
|
||||
"thumbnailUrl",
|
||||
"Test",
|
||||
"in Files",
|
||||
"http://localhost/nc/index.php/apps/files/?dir=/Files&scrollto=Test",
|
||||
"icon",
|
||||
false
|
||||
)
|
||||
),
|
||||
hasMoreResults = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
shortSleep()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search() {
|
||||
val activity = testActivityRule.launchActivity(null) as TestActivity
|
||||
val sut = UnifiedSearchFragment.newInstance(null, null)
|
||||
val testViewModel = UnifiedSearchViewModel(activity.application)
|
||||
testViewModel.setConnectivityService(activity.connectivityServiceMock)
|
||||
val localRepository = UnifiedSearchFakeRepository()
|
||||
testViewModel.setRepository(localRepository)
|
||||
|
||||
val ocFile = OCFile("/folder/test1.txt").apply {
|
||||
storagePath = "/sdcard/1.txt"
|
||||
storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
File(ocFile.storagePath).createNewFile()
|
||||
|
||||
activity.addFragment(sut)
|
||||
|
||||
shortSleep()
|
||||
|
||||
UiThreadStatement.runOnUiThread {
|
||||
sut.setViewModel(testViewModel)
|
||||
sut.vm.setQuery("test")
|
||||
sut.vm.initialQuery()
|
||||
}
|
||||
shortSleep()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.helpers
|
||||
|
||||
import com.owncloud.android.MainApp
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class FileOperationsHelperIT {
|
||||
|
||||
@Test
|
||||
fun testNull() {
|
||||
MainApp.setStoragePath(null)
|
||||
assertEquals(-1L, FileOperationsHelper.getAvailableSpaceOnDevice())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonExistingPath() {
|
||||
MainApp.setStoragePath("/123/123")
|
||||
assertEquals(-1L, FileOperationsHelper.getAvailableSpaceOnDevice())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExistingPath() {
|
||||
MainApp.setStoragePath("/sdcard/")
|
||||
assertTrue(FileOperationsHelper.getAvailableSpaceOnDevice() > 0L)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.helpers
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.test.core.app.launchActivity
|
||||
import com.nextcloud.client.jobs.upload.FileUploadWorker
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class UriUploaderIT : AbstractIT() {
|
||||
|
||||
@Test
|
||||
fun testUploadPrivatePathSharedPreferences() {
|
||||
launchActivity<TestActivity>().use { scenario ->
|
||||
scenario.onActivity { activity ->
|
||||
val packageName = activity.packageName
|
||||
val path = "file:///data/data/$packageName/shared_prefs/com.nextcloud.client_preferences.xml"
|
||||
testPrivatePath(activity, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUploadPrivatePathUserFile() {
|
||||
launchActivity<TestActivity>().use { scenario ->
|
||||
scenario.onActivity { activity ->
|
||||
val packageName = activity.packageName
|
||||
val path = "file:///storage/emulated/0/Android/media/$packageName/nextcloud/test/welcome.txt"
|
||||
testPrivatePath(activity, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun testPrivatePath(activity: TestActivity, path: String) {
|
||||
val sut = UriUploader(
|
||||
activity,
|
||||
listOf(Uri.parse(path)),
|
||||
"",
|
||||
activity.user.orElseThrow(::RuntimeException),
|
||||
FileUploadWorker.LOCAL_BEHAVIOUR_MOVE,
|
||||
false,
|
||||
null
|
||||
)
|
||||
val uploadResult = sut.uploadUris()
|
||||
Assert.assertEquals(
|
||||
"Wrong result code",
|
||||
UriUploader.UriUploaderResultCode.ERROR_SENSITIVE_PATH,
|
||||
uploadResult
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.preview
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class PreviewBitmapScreenshotIT : AbstractIT() {
|
||||
|
||||
companion object {
|
||||
private const val PNG_FILE_ASSET = "imageFile.png"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(PreviewBitmapActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showBitmap() {
|
||||
val pngFile = getFile(PNG_FILE_ASSET)
|
||||
|
||||
val activity = testActivityRule.launchActivity(
|
||||
Intent().putExtra(
|
||||
PreviewBitmapActivity.EXTRA_BITMAP_PATH,
|
||||
pngFile.absolutePath
|
||||
)
|
||||
)
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(activity)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.preview
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import org.junit.Rule
|
||||
|
||||
class PreviewImageFragmentIT : AbstractIT() {
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
// Disabled for now due to strange failing when using entire test suite
|
||||
// Findings so far:
|
||||
// PreviewImageFragmentIT runs fine when only running this
|
||||
// running it in whole test suite fails
|
||||
// manually tried to execute LoadBitmapTask, but this does not start "doInBackground", but only creates class
|
||||
|
||||
// @Test
|
||||
// @ScreenshotTest
|
||||
// fun corruptImage() {
|
||||
// val activity = testActivityRule.launchActivity(null)
|
||||
//
|
||||
// val ocFile = OCFile("/test.png")
|
||||
// val sut = PreviewImageFragment.newInstance(ocFile, true, false)
|
||||
//
|
||||
// activity.addFragment(sut)
|
||||
//
|
||||
// while (!sut.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
||||
// shortSleep()
|
||||
// }
|
||||
//
|
||||
// screenshot(activity)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// @ScreenshotTest
|
||||
// fun validImage() {
|
||||
// val activity = testActivityRule.launchActivity(null)
|
||||
//
|
||||
// val ocFile = OCFile("/test.png")
|
||||
// ocFile.storagePath = getFile("imageFile.png").absolutePath
|
||||
//
|
||||
// val sut = PreviewImageFragment.newInstance(ocFile, true, false)
|
||||
//
|
||||
// activity.addFragment(sut)
|
||||
//
|
||||
// while (!sut.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
||||
// shortSleep()
|
||||
// }
|
||||
//
|
||||
// screenshot(activity)
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.preview;
|
||||
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
import com.owncloud.android.utils.ScreenshotTest;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
public class PreviewTextFileFragmentTest extends AbstractIT {
|
||||
@Rule public IntentsTestRule<FileDisplayActivity> activityRule = new IntentsTestRule<>(FileDisplayActivity.class,
|
||||
true,
|
||||
false);
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void displaySimpleTextFile() throws IOException {
|
||||
FileDisplayActivity sut = activityRule.launchActivity(null);
|
||||
|
||||
shortSleep();
|
||||
|
||||
File file = getDummyFile("nonEmpty.txt");
|
||||
OCFile test = new OCFile("/text.md");
|
||||
test.setMimeType(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN);
|
||||
test.setStoragePath(file.getAbsolutePath());
|
||||
sut.startTextPreview(test, false);
|
||||
|
||||
shortSleep();
|
||||
|
||||
screenshot(sut);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
public void displayJavaSnippetFile() throws IOException {
|
||||
FileDisplayActivity sut = activityRule.launchActivity(null);
|
||||
|
||||
shortSleep();
|
||||
|
||||
File file = getFile("java.md");
|
||||
OCFile test = new OCFile("/java.md");
|
||||
test.setMimeType(MimeTypeUtil.MIMETYPE_TEXT_MARKDOWN);
|
||||
test.setStoragePath(file.getAbsolutePath());
|
||||
sut.startTextPreview(test, false);
|
||||
|
||||
shortSleep();
|
||||
|
||||
screenshot(sut);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.preview.pdf
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.test.espresso.intent.rule.IntentsTestRule
|
||||
import com.nextcloud.test.TestActivity
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class PreviewPdfFragmentScreenshotIT : AbstractIT() {
|
||||
|
||||
companion object {
|
||||
private const val PDF_FILE_ASSET = "test.pdf"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val testActivityRule = IntentsTestRule(TestActivity::class.java, true, false)
|
||||
|
||||
@Test
|
||||
@ScreenshotTest
|
||||
fun showPdf() {
|
||||
val activity = testActivityRule.launchActivity(null)
|
||||
|
||||
val pdfFile = getFile(PDF_FILE_ASSET)
|
||||
val ocFile = OCFile("/test.pdf").apply {
|
||||
storagePath = pdfFile.absolutePath
|
||||
}
|
||||
|
||||
val sut = PreviewPdfFragment.newInstance(ocFile)
|
||||
activity.addFragment(sut)
|
||||
|
||||
while (!sut.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
||||
shortSleep()
|
||||
}
|
||||
|
||||
activity.runOnUiThread {
|
||||
sut.dismissSnack()
|
||||
}
|
||||
|
||||
shortSleep()
|
||||
waitForIdleSync()
|
||||
|
||||
screenshot(activity)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.trashbin
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Intent
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.test.core.app.launchActivity
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.IdlingRegistry
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import com.owncloud.android.utils.EspressoIdlingResource
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.MainApp
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils
|
||||
import com.owncloud.android.utils.ScreenshotTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class TrashbinActivityIT : AbstractIT() {
|
||||
private val testClassName = "com.owncloud.android.ui.trashbin.TrashbinActivityIT"
|
||||
|
||||
enum class TestCase {
|
||||
ERROR, EMPTY, FILES
|
||||
}
|
||||
|
||||
@Before
|
||||
fun registerIdlingResource() {
|
||||
IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
|
||||
}
|
||||
|
||||
@After
|
||||
fun unregisterIdlingResource() {
|
||||
IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun error() {
|
||||
launchActivity<TrashbinActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
val trashbinRepository = TrashbinLocalRepository(TestCase.ERROR)
|
||||
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.loadFolder(
|
||||
onComplete = { EspressoIdlingResource.decrement() },
|
||||
onError = { EspressoIdlingResource.decrement() }
|
||||
)
|
||||
val screenShotName = createName(testClassName + "_" + "error", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun files() {
|
||||
launchActivity<TrashbinActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
val trashbinRepository = TrashbinLocalRepository(TestCase.FILES)
|
||||
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.loadFolder(
|
||||
onComplete = { EspressoIdlingResource.decrement() },
|
||||
onError = { EspressoIdlingResource.decrement() }
|
||||
)
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
val screenShotName = createName(testClassName + "_" + "files", "")
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun empty() {
|
||||
launchActivity<TrashbinActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
|
||||
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.loadFolder(
|
||||
onComplete = { EspressoIdlingResource.decrement() },
|
||||
onError = { EspressoIdlingResource.decrement() }
|
||||
)
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
val screenShotName = createName(testClassName + "_" + "empty", "")
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun loading() {
|
||||
launchActivity<TrashbinActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
|
||||
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.showInitialLoading()
|
||||
EspressoIdlingResource.decrement()
|
||||
val screenShotName = createName(testClassName + "_" + "loading", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun normalUser() {
|
||||
launchActivity<TrashbinActivity>().use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
|
||||
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.showUser()
|
||||
EspressoIdlingResource.decrement()
|
||||
val screenShotName = createName(testClassName + "_" + "normalUser", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThread
|
||||
@ScreenshotTest
|
||||
fun differentUser() {
|
||||
val temp = Account("differentUser@https://nextcloud.localhost", MainApp.getAccountType(targetContext))
|
||||
|
||||
AccountManager.get(targetContext).apply {
|
||||
addAccountExplicitly(temp, "password", null)
|
||||
setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "https://nextcloud.localhost")
|
||||
setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "differentUser")
|
||||
}
|
||||
|
||||
val intent = Intent(targetContext, TrashbinActivity::class.java).apply {
|
||||
putExtra(Intent.EXTRA_USER, "differentUser@https://nextcloud.localhost")
|
||||
}
|
||||
|
||||
launchActivity<TrashbinActivity>(intent).use { scenario ->
|
||||
scenario.onActivity { sut ->
|
||||
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
|
||||
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
|
||||
onIdleSync {
|
||||
EspressoIdlingResource.increment()
|
||||
sut.showUser()
|
||||
EspressoIdlingResource.decrement()
|
||||
val screenShotName = createName(testClassName + "_" + "differentUser", "")
|
||||
onView(isRoot()).check(matches(isDisplayed()))
|
||||
screenshotViaName(sut, screenShotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.ui.trashbin
|
||||
|
||||
import com.owncloud.android.R
|
||||
import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile
|
||||
import com.owncloud.android.ui.trashbin.TrashbinRepository.LoadFolderCallback
|
||||
|
||||
class TrashbinLocalRepository(private val testCase: TrashbinActivityIT.TestCase) : TrashbinRepository {
|
||||
override fun emptyTrashbin(callback: TrashbinRepository.OperationCallback?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun restoreFile(file: TrashbinFile?, callback: TrashbinRepository.OperationCallback?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun removeTrashbinFile(file: TrashbinFile?, callback: TrashbinRepository.OperationCallback?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun getFolder(remotePath: String?, callback: LoadFolderCallback?) {
|
||||
when (testCase) {
|
||||
TrashbinActivityIT.TestCase.ERROR -> callback?.onError(R.string.trashbin_loading_failed)
|
||||
TrashbinActivityIT.TestCase.FILES -> {
|
||||
val files = ArrayList<TrashbinFile>()
|
||||
files.add(
|
||||
TrashbinFile(
|
||||
"test.png",
|
||||
"image/png",
|
||||
"/trashbin/test.png",
|
||||
"subFolder/test.png",
|
||||
1395847838, // random date
|
||||
1395847908 // random date
|
||||
)
|
||||
)
|
||||
files.add(
|
||||
TrashbinFile(
|
||||
"image.jpg",
|
||||
"image/jpeg",
|
||||
"/trashbin/image.jpg",
|
||||
"image.jpg",
|
||||
1395841858, // random date
|
||||
1395837858 // random date
|
||||
)
|
||||
)
|
||||
files.add(
|
||||
TrashbinFile(
|
||||
"folder",
|
||||
"DIR",
|
||||
"/trashbin/folder/",
|
||||
"folder",
|
||||
1395347858, // random date
|
||||
1395849858 // random date
|
||||
)
|
||||
)
|
||||
|
||||
callback?.onSuccess(files)
|
||||
}
|
||||
TrashbinActivityIT.TestCase.EMPTY -> callback?.onSuccess(ArrayList<TrashbinFile>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,879 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2017 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.nextcloud.test.RandomStringGenerator;
|
||||
import com.nextcloud.test.RetryTestRule;
|
||||
import com.owncloud.android.AbstractIT;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.Data;
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile;
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1;
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedMetadata;
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.Encrypted;
|
||||
import com.owncloud.android.datamodel.e2e.v1.encrypted.EncryptedFolderMetadataFileV1;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.lib.resources.e2ee.CsrHelper;
|
||||
import com.owncloud.android.utils.EncryptionUtils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decodeStringToBase64Bytes;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptFile;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptFolderMetaData;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptPrivateKey;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptStringAsymmetric;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.decryptStringSymmetric;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.deserializeJSON;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.encodeBytesToBase64String;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.encryptFolderMetadata;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.generateChecksum;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.generateKey;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.generateSHA512;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.isFolderMigrated;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.ivDelimiter;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.ivDelimiterOld;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.ivLength;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.randomBytes;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.saltLength;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.serializeJSON;
|
||||
import static com.owncloud.android.utils.EncryptionUtils.verifySHA512;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class EncryptionTestIT extends AbstractIT {
|
||||
@Rule public RetryTestRule retryTestRule = new RetryTestRule();
|
||||
|
||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(targetContext);
|
||||
|
||||
private static final String MD5_ALGORITHM = "MD5";
|
||||
|
||||
private static final String filename = "ia7OEEEyXMoRa1QWQk8r";
|
||||
private static final String secondFilename = "n9WXAIXO2wRY4R8nXwmo";
|
||||
|
||||
public static final String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAo" +
|
||||
"IBAQDsn0JKS/THu328z1IgN0VzYU53HjSX03WJIgWkmyTaxbiKpoJaKbksXmfSpgzV" +
|
||||
"GzKFvGfZ03fwFrN7Q8P8R2e8SNiell7mh1TDw9/0P7Bt/ER8PJrXORo+GviKHxaLr7" +
|
||||
"Y0BJX9i/nW/L0L/VaE8CZTAqYBdcSJGgHJjY4UMf892ZPTa9T2Dl3ggdMZ7BQ2kiCi" +
|
||||
"CC3qV99b0igRJGmmLQaGiAflhFzuDQPMifUMq75wI8RSRPdxUAtjTfkl68QHu7Umye" +
|
||||
"yy33OQgdUKaTl5zcS3VSQbNjveVCNM4RDH1RlEc+7Wf1BY8APqT6jbiBcROJD2CeoL" +
|
||||
"H2eiIJCi+61ZkSGfAgMBAAECggEBALFStCHrhBf+GL9a+qer4/8QZ/X6i91PmaBX/7" +
|
||||
"SYk2jjjWVSXRNmex+V6+Y/jBRT2mvAgm8J+7LPwFdatE+lz0aZrMRD2gCWYF6Itpda" +
|
||||
"90OlLkmQPVWWtGTgX2ta2tF5r2iSGzk0IdoL8zw98Q2UzpOcw30KnWtFMxuxWk0mHq" +
|
||||
"pgp00g80cDWg3+RPbWOhdLp5bflQ36fKDfmjq05cGlIk6unnVyC5HXpvh4d4k2EWlX" +
|
||||
"rjGsndVBPCjGkZePlLRgDHxT06r+5XdJ+1CBDZgCsmjGz3M8uOHyCfVW0WhB7ynzDT" +
|
||||
"agVgz0iqpuhAi9sPt6iWWwpAnRw8cQgqEKw9bvKKECgYEA/WPi2PJtL6u/xlysh/H7" +
|
||||
"A717CId6fPHCMDace39ZNtzUzc0nT5BemlcF0wZ74NeJSur3Q395YzB+eBMLs5p8mA" +
|
||||
"95wgGvJhM65/J+HX+k9kt6Z556zLMvtG+j1yo4D0VEwm3xahB4SUUP+1kD7dNvo4+8" +
|
||||
"xeSCyjzNllvYZZC0DrECgYEA7w8pEqhHHn0a+twkPCZJS+gQTB9Rm+FBNGJqB3XpWs" +
|
||||
"TeLUxYRbVGk0iDve+eeeZ41drxcdyWP+WcL34hnrjgI1Fo4mK88saajpwUIYMy6+qM" +
|
||||
"LY+jC2NRSBox56eH7nsVYvQQK9eKqv9wbB+PF9SwOIvuETN7fd8mAY02UnoaaU8CgY" +
|
||||
"BoHRKocXPLkpZJuuppMVQiRUi4SHJbxDo19Tp2w+y0TihiJ1lvp7I3WGpcOt3LlMQk" +
|
||||
"tEbExSvrRZGxZKH6Og/XqwQsYuTEkEIz679F/5yYVosE6GkskrOXQAfh8Mb3/04xVV" +
|
||||
"tMaVgDQw0+CWVD4wyL+BNofGwBDNqsXTCdCsfxAQKBgQCDv2EtbRw0y1HRKv21QIxo" +
|
||||
"ju5cZW4+cDfVPN+eWPdQFOs1H7wOPsc0aGRiiupV2BSEF3O1ApKziEE5U1QH+29bR4" +
|
||||
"R8L1pemeGX8qCNj5bCubKjcWOz5PpouDcEqimZ3q98p3E6GEHN15UHoaTkx0yO/V8o" +
|
||||
"j6zhQ9fYRxDHB5ACtQKBgQCOO7TJUO1IaLTjcrwS4oCfJyRnAdz49L1AbVJkIBK0fh" +
|
||||
"JLecOFu3ZlQl/RStQb69QKb5MNOIMmQhg8WOxZxHcpmIDbkDAm/J/ovJXFSoBdOr5o" +
|
||||
"uQsYsDZhsWW97zvLMzg5pH9/3/1BNz5q3Vu4HgfBSwWGt4E2NENj+XA+QAVmGA==";
|
||||
|
||||
public static final String publicKey = "-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIDpzCCAo+gAwIBAgIBADANBgkqhkiG9w0BAQUFADBuMRowGAYDVQQDDBF3d3cu\n" +
|
||||
"bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0\n" +
|
||||
"dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw\n" +
|
||||
"HhcNMTcwOTI2MTAwNDMwWhcNMzcwOTIxMTAwNDMwWjBuMRowGAYDVQQDDBF3d3cu\n" +
|
||||
"bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0\n" +
|
||||
"dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw\n" +
|
||||
"ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsn0JKS/THu328z1IgN0Vz\n" +
|
||||
"YU53HjSX03WJIgWkmyTaxbiKpoJaKbksXmfSpgzVGzKFvGfZ03fwFrN7Q8P8R2e8\n" +
|
||||
"SNiell7mh1TDw9/0P7Bt/ER8PJrXORo+GviKHxaLr7Y0BJX9i/nW/L0L/VaE8CZT\n" +
|
||||
"AqYBdcSJGgHJjY4UMf892ZPTa9T2Dl3ggdMZ7BQ2kiCiCC3qV99b0igRJGmmLQaG\n" +
|
||||
"iAflhFzuDQPMifUMq75wI8RSRPdxUAtjTfkl68QHu7Umyeyy33OQgdUKaTl5zcS3\n" +
|
||||
"VSQbNjveVCNM4RDH1RlEc+7Wf1BY8APqT6jbiBcROJD2CeoLH2eiIJCi+61ZkSGf\n" +
|
||||
"AgMBAAGjUDBOMB0GA1UdDgQWBBTFrXz2tk1HivD9rQ75qeoyHrAgIjAfBgNVHSME\n" +
|
||||
"GDAWgBTFrXz2tk1HivD9rQ75qeoyHrAgIjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\n" +
|
||||
"DQEBBQUAA4IBAQARQTX21QKO77gAzBszFJ6xVnjfa23YZF26Z4X1KaM8uV8TGzuN\n" +
|
||||
"JA95XmReeP2iO3r8EWXS9djVCD64m2xx6FOsrUI8HZaw1JErU8mmOaLAe8q9RsOm\n" +
|
||||
"9Eq37e4vFp2YUEInYUqs87ByUcA4/8g3lEYeIUnRsRsWsA45S3wD7wy07t+KAn7j\n" +
|
||||
"yMmfxdma6hFfG9iN/egN6QXUAyIPXvUvlUuZ7/BhWBj/3sHMrF9quy9Q2DOI8F3t\n" +
|
||||
"1wdQrkq4BtStKhciY5AIXz9SqsctFHTv4Lwgtkapoel4izJnO0ZqYTXVe7THwri9\n" +
|
||||
"H/gua6uJDWH9jk2/CiZDWfsyFuNUuXvDSp05\n" +
|
||||
"-----END CERTIFICATE-----";
|
||||
|
||||
@Test
|
||||
public void encryptStringAsymmetric() throws Exception {
|
||||
byte[] key1 = generateKey();
|
||||
String base64encodedKey = encodeBytesToBase64String(key1);
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringAsymmetric(base64encodedKey, publicKey);
|
||||
String decryptedString = decryptStringAsymmetric(encryptedString, privateKey);
|
||||
|
||||
byte[] key2 = decodeStringToBase64Bytes(decryptedString);
|
||||
|
||||
assertTrue(Arrays.equals(key1, key2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptStringAsymmetricCorrectPublicKey() throws Exception {
|
||||
KeyPair keyPair = EncryptionUtils.generateKeyPair();
|
||||
|
||||
byte[] key1 = generateKey();
|
||||
String base64encodedKey = encodeBytesToBase64String(key1);
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringAsymmetric(base64encodedKey, keyPair.getPublic());
|
||||
String decryptedString = decryptStringAsymmetric(encryptedString, keyPair.getPrivate());
|
||||
|
||||
byte[] key2 = decodeStringToBase64Bytes(decryptedString);
|
||||
|
||||
assertTrue(Arrays.equals(key1, key2));
|
||||
}
|
||||
|
||||
@Test(expected = BadPaddingException.class)
|
||||
public void encryptStringAsymmetricWrongPublicKey() throws Exception {
|
||||
KeyPair keyPair1 = EncryptionUtils.generateKeyPair();
|
||||
KeyPair keyPair2 = EncryptionUtils.generateKeyPair();
|
||||
|
||||
byte[] key1 = generateKey();
|
||||
String base64encodedKey = encodeBytesToBase64String(key1);
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringAsymmetric(base64encodedKey, keyPair1.getPublic());
|
||||
decryptStringAsymmetric(encryptedString, keyPair2.getPrivate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModulus() throws Exception {
|
||||
KeyPair keyPair = EncryptionUtils.generateKeyPair();
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
|
||||
|
||||
BigInteger modulusPublic = publicKey.getModulus();
|
||||
BigInteger modulusPrivate = privateKey.getModulus();
|
||||
|
||||
assertEquals(modulusPrivate, modulusPublic);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptStringSymmetricRandom() throws Exception {
|
||||
int max = 500;
|
||||
for (int i = 0; i < max; i++) {
|
||||
Log_OC.d("EncryptionTestIT", i + " of " + max);
|
||||
byte[] key = generateKey();
|
||||
|
||||
String encryptedString;
|
||||
if (new Random().nextBoolean()) {
|
||||
encryptedString = EncryptionUtils.encryptStringSymmetricAsString(privateKey, key);
|
||||
} else {
|
||||
encryptedString = EncryptionUtils.encryptStringSymmetricAsStringOld(privateKey, key);
|
||||
|
||||
if (encryptedString.indexOf(ivDelimiterOld) != encryptedString.lastIndexOf(ivDelimiterOld)) {
|
||||
Log_OC.d("EncryptionTestIT", "skip due to duplicated iv (old system) -> ignoring");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String decryptedString = decryptStringSymmetric(encryptedString, key);
|
||||
|
||||
assertEquals(privateKey, decryptedString);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptStringSymmetric() throws Exception {
|
||||
int max = 5000;
|
||||
byte[] key = generateKey();
|
||||
|
||||
for (int i = 0; i < max; i++) {
|
||||
Log_OC.d("EncryptionTestIT", i + " of " + max);
|
||||
|
||||
String encryptedString = EncryptionUtils.encryptStringSymmetricAsString(privateKey, key);
|
||||
|
||||
int delimiterPosition = encryptedString.indexOf(ivDelimiter);
|
||||
if (delimiterPosition == -1) {
|
||||
throw new RuntimeException("IV not found!");
|
||||
}
|
||||
|
||||
String ivString = encryptedString.substring(delimiterPosition + ivDelimiter.length());
|
||||
if (TextUtils.isEmpty(ivString)) {
|
||||
delimiterPosition = encryptedString.lastIndexOf(ivDelimiter);
|
||||
ivString = encryptedString.substring(delimiterPosition + ivDelimiter.length());
|
||||
|
||||
if (TextUtils.isEmpty(ivString)) {
|
||||
throw new RuntimeException("IV string is empty");
|
||||
}
|
||||
}
|
||||
|
||||
String decryptedString = decryptStringSymmetric(encryptedString, key);
|
||||
|
||||
assertEquals(privateKey, decryptedString);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encryptPrivateKey() throws Exception {
|
||||
int max = 10;
|
||||
for (int i = 0; i < max; i++) {
|
||||
Log_OC.d("EncryptionTestIT", i + " of " + max);
|
||||
|
||||
String keyPhrase = "moreovertelevisionfactorytendencyindependenceinternationalintellectualimpress" +
|
||||
"interestvolunteer";
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(4096, new SecureRandom());
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
byte[] privateKeyBytes = privateKey.getEncoded();
|
||||
String privateKeyString = encodeBytesToBase64String(privateKeyBytes);
|
||||
|
||||
String encryptedString;
|
||||
if (new Random().nextBoolean()) {
|
||||
encryptedString = EncryptionUtils.encryptPrivateKey(privateKeyString, keyPhrase);
|
||||
} else {
|
||||
encryptedString = EncryptionUtils.encryptPrivateKeyOld(privateKeyString, keyPhrase);
|
||||
}
|
||||
String decryptedString = decryptPrivateKey(encryptedString, keyPhrase);
|
||||
|
||||
assertEquals(privateKeyString, decryptedString);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateCSR() throws Exception {
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(2048, new SecureRandom());
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
|
||||
assertFalse(new CsrHelper().generateCsrPemEncodedString(keyPair, "").isEmpty());
|
||||
assertFalse(encodeBytesToBase64String(keyPair.getPublic().getEncoded()).isEmpty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DecryptedFolderMetadataFile -> EncryptedFolderMetadataFile -> JSON -> encrypt -> decrypt -> JSON ->
|
||||
* EncryptedFolderMetadataFile -> DecryptedFolderMetadataFile
|
||||
*/
|
||||
@Test
|
||||
public void encryptionMetadataV1() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata1 = generateFolderMetadataV1_1();
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
1,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2,
|
||||
privateKey,
|
||||
arbitraryDataProvider,
|
||||
user,
|
||||
1);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangedMetadataKey() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata1 = generateFolderMetadataV1_1();
|
||||
long folderID = 1;
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// store metadata key
|
||||
String oldMetadataKey = encryptedFolderMetadata1.getMetadata().getMetadataKey();
|
||||
|
||||
// do it again
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
String newMetadataKey = encryptedFolderMetadata2.getMetadata().getMetadataKey();
|
||||
|
||||
assertNotEquals(oldMetadataKey, newMetadataKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateMetadataKey() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata1 = generateFolderMetadataV1_1();
|
||||
long folderID = 1;
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// reset new metadata key, to mimic old version
|
||||
encryptedFolderMetadata1.getMetadata().setMetadataKey(null);
|
||||
String oldMetadataKey = encryptedFolderMetadata1.getMetadata().getMetadataKey();
|
||||
|
||||
// do it again
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
String newMetadataKey = encryptedFolderMetadata2.getMetadata().getMetadataKey();
|
||||
|
||||
assertNotEquals(oldMetadataKey, newMetadataKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCryptFileWithoutMetadata() throws Exception {
|
||||
byte[] key = decodeStringToBase64Bytes("WANM0gRv+DhaexIsI0T3Lg==");
|
||||
byte[] iv = decodeStringToBase64Bytes("gKm3n+mJzeY26q4OfuZEqg==");
|
||||
|
||||
assertTrue(cryptFile(filename, "78f42172166f9dc8fd1a7156b1753353", key, iv));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cryptFileWithMetadata() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 metadata = generateFolderMetadataV1_1();
|
||||
|
||||
assertTrue(cryptFile(filename,
|
||||
"78f42172166f9dc8fd1a7156b1753353",
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get(filename)
|
||||
.getEncrypted().getKey()),
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get(filename)
|
||||
.getInitializationVector())));
|
||||
|
||||
assertTrue(cryptFile(secondFilename,
|
||||
"825143ed1f21ebb0c3b3c3f005b2f5db",
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get(secondFilename)
|
||||
.getEncrypted().getKey()),
|
||||
decodeStringToBase64Bytes(metadata.getFiles().get(secondFilename)
|
||||
.getInitializationVector())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bigMetadata() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata1 = generateFolderMetadataV1_1();
|
||||
long folderID = 1;
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2,
|
||||
privateKey,
|
||||
arbitraryDataProvider,
|
||||
user,
|
||||
folderID);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
|
||||
// prefill with 500
|
||||
for (int i = 0; i < 500; i++) {
|
||||
addFile(decryptedFolderMetadata1, i);
|
||||
}
|
||||
|
||||
int max = 505;
|
||||
for (int i = 500; i < max; i++) {
|
||||
Log_OC.d(this, "Big metadata: " + i + " of " + max);
|
||||
|
||||
addFile(decryptedFolderMetadata1, i);
|
||||
|
||||
// encrypt
|
||||
encryptedFolderMetadata1 = encryptFolderMetadata(decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// serialize
|
||||
encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
decryptedFolderMetadata2 = decryptFolderMetaData(encryptedFolderMetadata2,
|
||||
privateKey,
|
||||
arbitraryDataProvider,
|
||||
user,
|
||||
folderID);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
|
||||
assertEquals(i + 3, decryptedFolderMetadata1.getFiles().size());
|
||||
assertEquals(i + 3, decryptedFolderMetadata2.getFiles().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bigMetadata2() throws Exception {
|
||||
long folderID = 1;
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata1 = generateFolderMetadataV1_1();
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<EncryptedFolderMetadataFileV1>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2,
|
||||
privateKey,
|
||||
arbitraryDataProvider,
|
||||
user,
|
||||
folderID);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
|
||||
// prefill with 500
|
||||
for (int i = 0; i < 500; i++) {
|
||||
addFile(decryptedFolderMetadata1, i);
|
||||
}
|
||||
|
||||
int max = 505;
|
||||
for (int i = 500; i < max; i++) {
|
||||
Log_OC.d(this, "Big metadata: " + i + " of " + max);
|
||||
|
||||
addFile(decryptedFolderMetadata1, i);
|
||||
|
||||
// encrypt
|
||||
encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
|
||||
// serialize
|
||||
encryptedJson = serializeJSON(encryptedFolderMetadata1);
|
||||
|
||||
// de-serialize
|
||||
encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2,
|
||||
privateKey,
|
||||
arbitraryDataProvider,
|
||||
user,
|
||||
folderID);
|
||||
|
||||
// compare
|
||||
assertTrue(compareJsonStrings(serializeJSON(decryptedFolderMetadata1),
|
||||
serializeJSON(decryptedFolderMetadata2)));
|
||||
|
||||
assertEquals(i + 3, decryptedFolderMetadata1.getFiles().size());
|
||||
assertEquals(i + 3, decryptedFolderMetadata2.getFiles().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filedrop() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata1 = generateFolderMetadataV1_1();
|
||||
long folderID = 1;
|
||||
|
||||
// add filedrop
|
||||
Map<String, DecryptedFile> filesdrop = new HashMap<>();
|
||||
|
||||
Data data = new Data();
|
||||
data.setKey("9dfzbIYDt28zTyZfbcll+g==");
|
||||
data.setFilename("test2.txt");
|
||||
data.setVersion(1);
|
||||
|
||||
DecryptedFile file = new DecryptedFile();
|
||||
file.setInitializationVector("hnJLF8uhDvDoFK4ajuvwrg==");
|
||||
file.setEncrypted(data);
|
||||
file.setMetadataKey(0);
|
||||
file.setAuthenticationTag("qOQZdu5soFO77Y7y4rAOVA==");
|
||||
|
||||
filesdrop.put("eie8iaeiaes8e87td6", file);
|
||||
|
||||
decryptedFolderMetadata1.setFiledrop(filesdrop);
|
||||
|
||||
// encrypt
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata1 = encryptFolderMetadata(
|
||||
decryptedFolderMetadata1,
|
||||
publicKey,
|
||||
folderID,
|
||||
user,
|
||||
arbitraryDataProvider);
|
||||
EncryptionUtils.encryptFileDropFiles(decryptedFolderMetadata1, encryptedFolderMetadata1, publicKey);
|
||||
|
||||
// serialize
|
||||
String encryptedJson = serializeJSON(encryptedFolderMetadata1, true);
|
||||
|
||||
// de-serialize
|
||||
EncryptedFolderMetadataFileV1 encryptedFolderMetadata2 = deserializeJSON(encryptedJson,
|
||||
new TypeToken<>() {
|
||||
});
|
||||
|
||||
// decrypt
|
||||
DecryptedFolderMetadataFileV1 decryptedFolderMetadata2 = decryptFolderMetaData(
|
||||
encryptedFolderMetadata2,
|
||||
privateKey,
|
||||
arbitraryDataProvider,
|
||||
user,
|
||||
folderID);
|
||||
|
||||
// compare
|
||||
assertFalse(compareJsonStrings(serializeJSON(decryptedFolderMetadata1, true),
|
||||
serializeJSON(decryptedFolderMetadata2, true)));
|
||||
|
||||
assertEquals(decryptedFolderMetadata1.getFiles().size() + decryptedFolderMetadata1.getFiledrop().size(),
|
||||
decryptedFolderMetadata2.getFiles().size());
|
||||
|
||||
// no filedrop content means null
|
||||
assertNull(decryptedFolderMetadata2.getFiledrop());
|
||||
}
|
||||
|
||||
private void addFile(DecryptedFolderMetadataFileV1 decryptedFolderMetadata, int counter) {
|
||||
// Add new file
|
||||
// Always generate new
|
||||
byte[] key = generateKey();
|
||||
byte[] iv = randomBytes(ivLength);
|
||||
byte[] authTag = randomBytes((128 / 8));
|
||||
|
||||
Data data = new Data();
|
||||
data.setKey(EncryptionUtils.encodeBytesToBase64String(key));
|
||||
data.setFilename(counter + ".txt");
|
||||
data.setVersion(1);
|
||||
|
||||
DecryptedFile file = new DecryptedFile();
|
||||
file.setInitializationVector(EncryptionUtils.encodeBytesToBase64String(iv));
|
||||
file.setEncrypted(data);
|
||||
file.setMetadataKey(0);
|
||||
file.setAuthenticationTag(EncryptionUtils.encodeBytesToBase64String(authTag));
|
||||
|
||||
decryptedFolderMetadata.getFiles().put(RandomStringGenerator.make(20), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates new keys and tests if they are unique
|
||||
*/
|
||||
@Test
|
||||
public void testKey() {
|
||||
Set<String> keys = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertTrue(keys.add(encodeBytesToBase64String(generateKey())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generates new ivs and tests if they are unique
|
||||
*/
|
||||
@Test
|
||||
public void testIV() {
|
||||
Set<String> ivs = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertTrue(ivs.add(encodeBytesToBase64String(
|
||||
randomBytes(ivLength))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generates new salt and tests if they are unique
|
||||
*/
|
||||
@Test
|
||||
public void testSalt() {
|
||||
Set<String> ivs = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assertTrue(ivs.add(encodeBytesToBase64String(
|
||||
randomBytes(saltLength))));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSHA512() {
|
||||
// sent to 3rd party app in cleartext
|
||||
String token = "4ae5978bf5354cd284b539015d442141";
|
||||
String salt = encodeBytesToBase64String(randomBytes(saltLength));
|
||||
|
||||
// stored in database
|
||||
String hashedToken = generateSHA512(token, salt);
|
||||
|
||||
// check: use passed cleartext and salt to verify hashed token
|
||||
assertTrue(verifySHA512(hashedToken, token));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExcludeGSON() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 metadata = generateFolderMetadataV1_1();
|
||||
|
||||
String jsonWithKeys = serializeJSON(metadata);
|
||||
String jsonWithoutKeys = serializeJSON(metadata, true);
|
||||
|
||||
assertTrue(jsonWithKeys.contains("metadataKeys"));
|
||||
assertFalse(jsonWithoutKeys.contains("metadataKeys"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsSign() {
|
||||
assertEquals("\"===\"", serializeJSON("==="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64() {
|
||||
String originalString = "randomstring123";
|
||||
|
||||
String encodedString = EncryptionUtils.encodeStringToBase64String(originalString);
|
||||
String compare = EncryptionUtils.decodeBase64StringToString(encodedString);
|
||||
assertEquals(originalString, compare);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksum() throws Exception {
|
||||
DecryptedFolderMetadataFileV1 metadata = new DecryptedFolderMetadataFileV1();
|
||||
String mnemonic = "chimney potato joke science ridge trophy result estate spare vapor much room";
|
||||
|
||||
metadata.getFiles().put(secondFilename, new DecryptedFile());
|
||||
metadata.getFiles().put(filename, new DecryptedFile());
|
||||
|
||||
String encryptedMetadataKey = "GuFPAULudgD49S4+VDFck3LiqQ8sx4zmbrBtdpCSGcT+T0W0z4F5gYQYPlzTG6WOkdW5LJZK/";
|
||||
metadata.getMetadata().setMetadataKey(encryptedMetadataKey);
|
||||
|
||||
String checksum = generateChecksum(metadata, mnemonic);
|
||||
|
||||
String expectedChecksum = "002cefa6493f2efb0192247a34bb1b16d391aefee968fd3d4225c4ec3cd56436";
|
||||
assertEquals(expectedChecksum, checksum);
|
||||
|
||||
// change something
|
||||
String newMnemonic = mnemonic + "1";
|
||||
|
||||
String newChecksum = generateChecksum(metadata, newMnemonic);
|
||||
assertNotEquals(expectedChecksum, newChecksum);
|
||||
|
||||
metadata.getFiles().put("aeb34yXMoRa1QWQk8r", new DecryptedFile());
|
||||
|
||||
newChecksum = generateChecksum(metadata, mnemonic);
|
||||
assertNotEquals(expectedChecksum, newChecksum);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIdToMigratedIds() {
|
||||
// delete ids
|
||||
arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), EncryptionUtils.MIGRATED_FOLDER_IDS);
|
||||
|
||||
long id = 1;
|
||||
EncryptionUtils.addIdToMigratedIds(id, user, arbitraryDataProvider);
|
||||
|
||||
assertTrue(isFolderMigrated(id, user, arbitraryDataProvider));
|
||||
}
|
||||
|
||||
// TODO E2E: more tests
|
||||
|
||||
// more tests
|
||||
// migrate v1 -> v2
|
||||
// migrate v1 -> v2 with filedrop
|
||||
|
||||
// migrate v1 -> v1.1
|
||||
// migrate v1 -> v1.1 with filedrop
|
||||
|
||||
// migrate v1.1 -> v2
|
||||
// migrate v1.1 -> v2 with filedrop
|
||||
|
||||
|
||||
// Helper
|
||||
public static boolean compareJsonStrings(String expected, String actual) {
|
||||
JsonElement o1 = JsonParser.parseString(expected);
|
||||
JsonElement o2 = JsonParser.parseString(actual);
|
||||
|
||||
if (o1.equals(o2)) {
|
||||
return true;
|
||||
} else {
|
||||
System.out.println("expected: " + o1);
|
||||
System.out.println("actual: " + o2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private DecryptedFolderMetadataFileV1 generateFolderMetadataV1_1() throws Exception {
|
||||
String metadataKey0 = encodeBytesToBase64String(generateKey());
|
||||
String metadataKey1 = encodeBytesToBase64String(generateKey());
|
||||
String metadataKey2 = encodeBytesToBase64String(generateKey());
|
||||
HashMap<Integer, String> metadataKeys = new HashMap<>();
|
||||
metadataKeys.put(0, EncryptionUtils.encryptStringAsymmetric(metadataKey0, publicKey));
|
||||
metadataKeys.put(1, EncryptionUtils.encryptStringAsymmetric(metadataKey1, publicKey));
|
||||
metadataKeys.put(2, EncryptionUtils.encryptStringAsymmetric(metadataKey2, publicKey));
|
||||
Encrypted encrypted = new Encrypted();
|
||||
encrypted.setMetadataKeys(metadataKeys);
|
||||
|
||||
DecryptedMetadata metadata1 = new DecryptedMetadata();
|
||||
metadata1.setMetadataKeys(metadataKeys);
|
||||
metadata1.setVersion(1);
|
||||
|
||||
HashMap<String, DecryptedFile> files = new HashMap<>();
|
||||
|
||||
Data data1 = new Data();
|
||||
data1.setKey("WANM0gRv+DhaexIsI0T3Lg==");
|
||||
data1.setFilename("test.txt");
|
||||
data1.setVersion(1);
|
||||
|
||||
DecryptedFile file1 = new DecryptedFile();
|
||||
file1.setInitializationVector("gKm3n+mJzeY26q4OfuZEqg==");
|
||||
file1.setEncrypted(data1);
|
||||
file1.setMetadataKey(0);
|
||||
file1.setAuthenticationTag("PboI9tqHHX3QeAA22PIu4w==");
|
||||
|
||||
files.put(filename, file1);
|
||||
|
||||
Data data2 = new Data();
|
||||
data2.setKey("9dfzbIYDt28zTyZfbcll+g==");
|
||||
data2.setFilename("test2.txt");
|
||||
data2.setVersion(1);
|
||||
|
||||
DecryptedFile file2 = new DecryptedFile();
|
||||
file2.setInitializationVector("hnJLF8uhDvDoFK4ajuvwrg==");
|
||||
file2.setEncrypted(data2);
|
||||
file2.setMetadataKey(0);
|
||||
file2.setAuthenticationTag("qOQZdu5soFO77Y7y4rAOVA==");
|
||||
|
||||
files.put(secondFilename, file2);
|
||||
|
||||
return new DecryptedFolderMetadataFileV1(metadata1, files);
|
||||
}
|
||||
|
||||
private boolean cryptFile(String fileName, String md5, byte[] key, byte[] iv)
|
||||
throws Exception {
|
||||
File file = File.createTempFile(fileName, "enc");
|
||||
String md5BeforeEncryption = getMD5Sum(file);
|
||||
|
||||
// Encryption
|
||||
Cipher encryptorCipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv);
|
||||
EncryptionUtils.encryptFile(user.getAccountName(), file, encryptorCipher);
|
||||
String encryptorCipherAuthTag = EncryptionUtils.getAuthenticationTag(encryptorCipher);
|
||||
|
||||
// Decryption
|
||||
Cipher decryptorCipher = EncryptionUtils.getCipher(Cipher.DECRYPT_MODE, key, iv);
|
||||
File decryptedFile = File.createTempFile("file", "dec");
|
||||
decryptFile(decryptorCipher, file, decryptedFile, encryptorCipherAuthTag, new ArbitraryDataProviderImpl(targetContext), user);
|
||||
|
||||
String md5AfterEncryption = getMD5Sum(decryptedFile);
|
||||
|
||||
if (md5BeforeEncryption == null) {
|
||||
Assert.fail();
|
||||
}
|
||||
|
||||
return md5BeforeEncryption.equals(md5AfterEncryption);
|
||||
}
|
||||
|
||||
public static String getMD5Sum(File file) {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM);
|
||||
DigestInputStream dis = new DigestInputStream(fis, md);
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = dis.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, bytesRead);
|
||||
}
|
||||
byte[] digest = md.digest();
|
||||
return bytesToHex(digest);
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Chris Narkiewicz <hello@ezaquarii.com>
|
||||
* SPDX-FileCopyrightText: 2019-2021 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import com.nextcloud.client.account.MockUser;
|
||||
import com.nextcloud.client.account.User;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.operations.RemoveFileOperation;
|
||||
import com.owncloud.android.utils.ErrorMessageAdapter;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ErrorMessageAdapterIT {
|
||||
private final static String PATH_TO_DELETE = "/path/to/a.file";
|
||||
private final static String EXPECTED_ERROR_MESSAGE = "You are not permitted to delete this file";
|
||||
private final static String ACCOUNT_TYPE = "nextcloud";
|
||||
|
||||
@Test
|
||||
public void getErrorCauseMessageForForbiddenRemoval() {
|
||||
Resources resources = InstrumentationRegistry.getInstrumentation().getTargetContext().getResources();
|
||||
User user = new MockUser("name", ACCOUNT_TYPE);
|
||||
Context context = MainApp.getAppContext();
|
||||
|
||||
String errorMessage = ErrorMessageAdapter.getErrorCauseMessage(
|
||||
new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN),
|
||||
new RemoveFileOperation(new OCFile(PATH_TO_DELETE),
|
||||
false,
|
||||
user,
|
||||
false,
|
||||
context,
|
||||
new FileDataStorageManager(user, context.getContentResolver())),
|
||||
resources
|
||||
);
|
||||
|
||||
assertEquals(EXPECTED_ERROR_MESSAGE, errorMessage);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Test
|
||||
|
||||
class BitmapUtilsIT {
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun usernameToColor() {
|
||||
assertEquals(BitmapUtils.Color(0, 0, 0), BitmapUtils.Color(0, 0, 0))
|
||||
assertEquals(BitmapUtils.Color(221, 203, 85), BitmapUtils.usernameToColor("User"))
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.usernameToColor("Admin"))
|
||||
assertEquals(BitmapUtils.Color(0, 130, 201), BitmapUtils.usernameToColor(""))
|
||||
assertEquals(BitmapUtils.Color(201, 136, 121), BitmapUtils.usernameToColor("68b329da9893e34099c7d8ad5cb9c940"))
|
||||
|
||||
// tests from server
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.usernameToColor("Alishia Ann Lowry"))
|
||||
assertEquals(BitmapUtils.Color(0, 130, 201), BitmapUtils.usernameToColor("Arham Johnson"))
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.usernameToColor("Brayden Truong"))
|
||||
assertEquals(BitmapUtils.Color(151, 80, 164), BitmapUtils.usernameToColor("Daphne Roy"))
|
||||
assertEquals(BitmapUtils.Color(195, 114, 133), BitmapUtils.usernameToColor("Ellena Wright Frederic Conway"))
|
||||
assertEquals(BitmapUtils.Color(214, 180, 97), BitmapUtils.usernameToColor("Gianluca Hills"))
|
||||
assertEquals(BitmapUtils.Color(214, 180, 97), BitmapUtils.usernameToColor("Haseeb Stephens"))
|
||||
assertEquals(BitmapUtils.Color(151, 80, 164), BitmapUtils.usernameToColor("Idris Mac"))
|
||||
assertEquals(BitmapUtils.Color(0, 130, 201), BitmapUtils.usernameToColor("Kristi Fisher"))
|
||||
assertEquals(BitmapUtils.Color(188, 92, 145), BitmapUtils.usernameToColor("Lillian Wall"))
|
||||
assertEquals(BitmapUtils.Color(221, 203, 85), BitmapUtils.usernameToColor("Lorelai Taylor"))
|
||||
assertEquals(BitmapUtils.Color(151, 80, 164), BitmapUtils.usernameToColor("Madina Knight"))
|
||||
assertEquals(BitmapUtils.Color(121, 90, 171), BitmapUtils.usernameToColor("Rae Hope"))
|
||||
assertEquals(BitmapUtils.Color(188, 92, 145), BitmapUtils.usernameToColor("Santiago Singleton"))
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.usernameToColor("Sid Combs"))
|
||||
assertEquals(BitmapUtils.Color(30, 120, 193), BitmapUtils.usernameToColor("Vivienne Jacobs"))
|
||||
assertEquals(BitmapUtils.Color(110, 166, 143), BitmapUtils.usernameToColor("Zaki Cortes"))
|
||||
assertEquals(BitmapUtils.Color(91, 100, 179), BitmapUtils.usernameToColor("a user"))
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.usernameToColor("admin"))
|
||||
assertEquals(BitmapUtils.Color(151, 80, 164), BitmapUtils.usernameToColor("admin@cloud.example.com"))
|
||||
assertEquals(BitmapUtils.Color(221, 203, 85), BitmapUtils.usernameToColor("another user"))
|
||||
assertEquals(BitmapUtils.Color(36, 142, 181), BitmapUtils.usernameToColor("asd"))
|
||||
assertEquals(BitmapUtils.Color(0, 130, 201), BitmapUtils.usernameToColor("bar"))
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.usernameToColor("foo"))
|
||||
assertEquals(BitmapUtils.Color(182, 70, 157), BitmapUtils.usernameToColor("wasd"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun checkEqual() {
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.Color(208, 158, 109))
|
||||
assertNotEquals(BitmapUtils.Color(208, 158, 109), BitmapUtils.Color(208, 158, 100))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("MagicNumber")
|
||||
fun checkHashCode() {
|
||||
assertEquals(BitmapUtils.Color(208, 158, 109).hashCode(), BitmapUtils.Color(208, 158, 109).hashCode())
|
||||
assertNotEquals(BitmapUtils.Color(208, 158, 109).hashCode(), BitmapUtils.Color(208, 158, 100).hashCode())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class DisplayUtilsIT : AbstractIT() {
|
||||
@Test
|
||||
fun testPixelToDP() {
|
||||
val px = 123
|
||||
val dp = DisplayUtils.convertPixelToDp(px, targetContext)
|
||||
val newPx = DisplayUtils.convertDpToPixel(dp, targetContext)
|
||||
|
||||
assertEquals(px.toLong(), newPx.toLong())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper_ozturk@proton.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.After
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class DrawableUtilTests {
|
||||
|
||||
private var sut: DrawableUtil? = null
|
||||
private var context: Context? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
sut = DrawableUtil()
|
||||
context = InstrumentationRegistry.getInstrumentation().context
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAddDrawableAsOverlayWhenGivenValidDrawablesShouldContainTwoDrawable() {
|
||||
val bitmap: Bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888)
|
||||
val drawable = BitmapDrawable(context?.resources, bitmap)
|
||||
|
||||
val layerDrawable = sut?.addDrawableAsOverlay(drawable, drawable)
|
||||
|
||||
if (layerDrawable == null) {
|
||||
fail("Layer drawable expected to be not null")
|
||||
}
|
||||
|
||||
assert(layerDrawable?.numberOfLayers == 2)
|
||||
}
|
||||
|
||||
@After
|
||||
fun destroy() {
|
||||
sut = null
|
||||
context = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedMetadata
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser
|
||||
import com.owncloud.android.lib.resources.status.E2EVersion
|
||||
|
||||
class EncryptionTestUtils {
|
||||
val t1PrivateKey =
|
||||
"MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQC1p8eYMFwGoi7geYzEwNbePRLL5LRhorAecFG3zkpLBwSi/QHkU4" +
|
||||
"u4uSegEbHgOfe73eKVOFdfFpw8wd5cvtY+4CzbX8bu+yrC+tFGcJ25/4VQ78Bl4MI0SvOmxDwuZNrg9SWgs9RwialKOsfCEyz0" +
|
||||
"SS8RstGNt5KZKn1e8z7V9X/eORPmOQ5KcIXHlMbAY3m4erBSKvhRZdqy+Dbnc0rZeZaKkoIMJH1OYfVto/ek12iIKF2YStPVzo" +
|
||||
"TgNsFelPDxeA/lltgf6qDVRD+ELydEncPIJwcv52D8ZitoEyEOfjDZW+rvvE02g1ZD1xPkDLpwltAsFCglCKvKBAWuhthFAgMB" +
|
||||
"AAECgf8BN1MLcq+6m8C1tzNvN/UDd9c0rUpexM6D5eC4O+6B7YGidEqIhHVIzUj0e2HUgpRBbURxsvF1FWdIT2gu7dnULtOGWQ" +
|
||||
"xNujJ0kGwXfAnqxh/rACDFb5TS3sJawEExC5yJw14bCEbE/0uBF5uiTU/U9AV7PKHlqAKsS2RtcwPNceB8zDu0hh/Mb/uS7274" +
|
||||
"TsxUllx0WzGZrozO1K6AlOete9rXmmpghpFTNVhxgf0pxe3hrK+tZGSL9di+Wft9eCvSbdG/FzeXgwVqmGtWU7kSB7FqstEEJO" +
|
||||
"4VpOSyEfcXGHTHwdZjrhBUuAcjWE8E0mCKa8htRE52czb3C0f7ZYkCgYEA5eH3vmHEgQjXzSSEtbmDLRq9X9SB7pIAIXHj2UuE" +
|
||||
"OTkLUJ/7xLTHqt82jqZaZzns1RZIJXKZjH85CswQp/py2/qD240KvA/N+ELZaciaV+Wg+m4+iHdi0DyPkaKaBtFG1nsR2GbVWO" +
|
||||
"1OsaTUZTG4D7RCUErU6XVmNPQKSk5uRA0CgYEAykskpX3KKuWq5nxV4vwgPmxz+uAfCtaGhcPEUg764SR+n0ODAvGiEJU7B0Q2" +
|
||||
"oX621pDOQeRfFufiMWfD8ByhErs1HFCmW69YPlR8qamfc8tHG5UM+r3bb49sDEYU4qr1Ji5Zzs4XgfmToKLbWdzzhaW6YxqO7N" +
|
||||
"ntIIh2169PPxkCgYBF2TAWl8xGTLKNcYAlW1XBObO6z24fWBtUDi/mEWz+mheXCtVMAoX8pFAGbgNgBBiy8k8/mZ+QMgPaBQE2" +
|
||||
"mQGXV3oDFsrhM4go298Fpl9HP8126lJz0pqinRQecyKL2cDFYKWedDh1Cb30ehnTGZVMqD/R97rTqMlCY7hQtZ4JbQKBgEXpLD" +
|
||||
"QJQeoLT0GybJgyXA5WuspT1EaRlxH5cwqM5MUUMLJnyYol6cVjXXAIcfzj5tpGVxHMk9Q9tR0v6DY+HqhzjEpJ0QRUl+GKnz6f" +
|
||||
"QVzqPpvYqhCptoFahpPDUIp5XJmiYSUoclVX5F4aikYHJx3kBYMkdYqDUgDxSGkHzBJZAoGAHV44xgTW02dgeB5GfDJVWCJKAU" +
|
||||
"GsYOFuUehKUBXSJ0929hdP0sjOQDJN3DEDISzmgdWX5NyLJxEYgFWNivpePjWCWzOzyua3nPSpvxPIUB7xh27gjT91glj1hEmy" +
|
||||
"sCd7+9yoMPiCXR7iigRycxegI/Krd39QzISSk9O0finfytU="
|
||||
|
||||
val t1PublicKey = """-----BEGIN CERTIFICATE-----
|
||||
MIIC6DCCAdCgAwIBAgIBADANBgkqhkiG9w0BAQUFADANMQswCQYDVQQDDAJ0MTAe
|
||||
Fw0yMzA3MjUwNzU3MTJaFw00MzA3MjAwNzU3MTJaMA0xCzAJBgNVBAMMAnQxMIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtafHmDBcBqIu4HmMxMDW3j0S
|
||||
y+S0YaKwHnBRt85KSwcEov0B5FOLuLknoBGx4Dn3u93ilThXXxacPMHeXL7WPuAs
|
||||
21/G7vsqwvrRRnCduf+FUO/AZeDCNErzpsQ8LmTa4PUloLPUcImpSjrHwhMs9Ekv
|
||||
EbLRjbeSmSp9XvM+1fV/3jkT5jkOSnCFx5TGwGN5uHqwUir4UWXasvg253NK2XmW
|
||||
ipKCDCR9TmH1baP3pNdoiChdmErT1c6E4DbBXpTw8XgP5ZbYH+qg1UQ/hC8nRJ3D
|
||||
yCcHL+dg/GYraBMhDn4w2Vvq77xNNoNWQ9cT5Ay6cJbQLBQoJQirygQFrobYRQID
|
||||
AQABo1MwUTAdBgNVHQ4EFgQUE9zCeA9/QMAtVgLxD23X6ZcodhMwHwYDVR0jBBgw
|
||||
FoAUE9zCeA9/QMAtVgLxD23X6ZcodhMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
|
||||
9w0BAQUFAAOCAQEAZdy/YjJlvnz3FQwxp6oVtMJccpdxveEPfLzgaverhtd/vP8O
|
||||
AvDzOLgQJHmrDS91SG503eU4cYGyuNKwd77OyTnqMg+GUEmJhGfPpSVrEIdh65jv
|
||||
q61T4oqBdehevVmBq54rGiwL0DGv1DlXQlwiJZP4qni2KnOEFcnvL3gVtRnQjXQ+
|
||||
kHvlMshkK6w021EMV5NfjG2zg67wC65rLaej5f6Ssp2S7g2VtmE4aXq1bjAuEbqk
|
||||
4TiyZHLDdsJuqzyGyyOpMV7i9ucXDoaZt9cGS9hT2vRxTrSH63vKR8Xeig9+stLw
|
||||
t9ONcUqCKP7hd8rajtxM4JIIRExwD8OkgARWGg==
|
||||
-----END CERTIFICATE-----"""
|
||||
|
||||
val johnPrivateKey =
|
||||
"""MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDuPcSvlhqElPQsCdJEuGmptGj4TUBWe33yu+ncOYR8Ec3M0H4NL0gE
|
||||
|ORJJcz9i18ByLpNzDy6NUGOtlf9YSat/zKdAfFiZJolKc/y4BPfTr8xx5ml2mu4Rz39LXRru+nnhluV3g1h2Z9LvWhUVUqAztz9W2H
|
||||
|H6uC7jx+7HNtYC9VgsVzHjuHPQMlOePPZlr9Hry5enF/Psn24RdiKqwCz8WhsOwtmW5PdHLLBVHAoF53URnFR4sgmLLGlS2GEZ8hvx
|
||||
|vdV/2NmhRWLebmCZziyklAe9gCR9lgfN32tqzyMG7VptBHFy7YJidWjpjSZPGEqFBL+fmCO/cTGJAXfCn9djAgMBAAECggEAV2QBCg
|
||||
|edopShHKZdoyeiWsX621o7B341LR0RI99VYc2GGGNCWcPGPwZQVvEXh0JtLXU4UTR4dw3OApbLG6+qYS7JCzaRqVwhcFYrlbT804Hh
|
||||
|FMbYWNFsEsxyfUqh3peyrbWUZsqfYI+lKHd61F+CtHW7nje3V6jISnXEeP78cgioKOX8gsCG8DEWsmaLrQz0PyMwdhucRfa8Bm6qeX
|
||||
|NY+wCMg8lyH/+OLlyCZTdkaWbTBBD5UXGbZly8iX17McmsYhdjFyx1l0NQnVMAYjOpXXEkeEixZpSfm3GYxmdaQqZFkpbI/FbQF0yD
|
||||
|7hLrGwiRTDcyPUz+QypUv8CZxpXbgQKBgQD3btuYmb+BpPZjryfa3worv/3XQCTs08V0TX3mDxHVQL95TgP+L8/Z/brxIMBNpwG1wk
|
||||
|iCWLYLer68+qioMTohuzeUx7hRKcoHa9ezW8m7m9AcPmAnzNticPYv835BQjEu/avU98rwIDihsYgxcjU3L7/P2ajVgUDigQxmE3gO
|
||||
|OwKBgQD2fXBLwch0P5g2GCyOPYvSgyF/umS7mcyUVTE4WOoJNDf8Q+Bx1dA2yAKQaoVghqW4uCfOAo/rERvGAYZ7fm7nFwx1jZ8ToT
|
||||
|dKZwybIFPjF/zkfuZLajYxVOPnzuQrsXnjcGg/ltMKZg3NqnGQGnD1S3eOhZ+dIOBmb7+jSO4A+QKBgASqnpGeNLJpPgxbPVEva62v
|
||||
|jUYF+6xLwimTXJB+MEPpWLMc+Y5NsInX8zKg/393atzWsS9kJOrKgdZmk8+4PfRs53ty2NMPCrRhIExNqtxS7/XYZ0/Y2TpeDwaQfQ
|
||||
|0WBn9wYVE+6yDkOq0x//OOx9ommGN/I2QDcAnVjTpPm7AJAoGAYT8cDsdlTnfIlY70BSpC/8q8bKgdFeaXz+3MfW6W5wqzC9O7uS2h
|
||||
|9/rxCAj+lhaJS1dcXOql3Rfi3Tu80vwOxR1SzQ4StKvmJHSDhLA8aFwOahemxBojR1M2lz4IxzQ94n12o5/dozygNYQJSdEkv6IGiT
|
||||
|QuxM8zuTZdZQ5g2AECgYAujetfkwgVW7/gumpMKytoY0VuTzF4Y/XZfqBMVIiPIuUl57JbDzrcx6YVXX3PavxNWmBLBmMq3SHMbdva
|
||||
|H7LnU/8rvkT8xRVLg/w/bRJc3Lb3oUjrdhkUQUYDoOfMoFA+ceZ2L6bnSXwm86KKV+xoXWpxAoL4AvdNrMhoWw3+yg=="""
|
||||
.trimMargin()
|
||||
|
||||
val johnPublicKey = """-----BEGIN CERTIFICATE-----
|
||||
MIIDkDCCAnigAwIBAgIBADANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJERTEb
|
||||
MBkGA1UECAwSQmFkZW4tV3VlcnR0ZW1iZXJnMRIwEAYDVQQHDAlTdHV0dGdhcnQx
|
||||
EjAQBgNVBAoMCU5leHRjbG91ZDENMAsGA1UEAwwEam9objAeFw0yMzA3MTQwNzM0
|
||||
NTZaFw00MzA3MDkwNzM0NTZaMGExCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRl
|
||||
bi1XdWVydHRlbWJlcmcxEjAQBgNVBAcMCVN0dXR0Z2FydDESMBAGA1UECgwJTmV4
|
||||
dGNsb3VkMQ0wCwYDVQQDDARqb2huMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEA7j3Er5YahJT0LAnSRLhpqbRo+E1AVnt98rvp3DmEfBHNzNB+DS9IBDkS
|
||||
SXM/YtfAci6Tcw8ujVBjrZX/WEmrf8ynQHxYmSaJSnP8uAT306/MceZpdpruEc9/
|
||||
S10a7vp54Zbld4NYdmfS71oVFVKgM7c/Vthx+rgu48fuxzbWAvVYLFcx47hz0DJT
|
||||
njz2Za/R68uXpxfz7J9uEXYiqsAs/FobDsLZluT3RyywVRwKBed1EZxUeLIJiyxp
|
||||
UthhGfIb8b3Vf9jZoUVi3m5gmc4spJQHvYAkfZYHzd9ras8jBu1abQRxcu2CYnVo
|
||||
6Y0mTxhKhQS/n5gjv3ExiQF3wp/XYwIDAQABo1MwUTAdBgNVHQ4EFgQUmTeILVuB
|
||||
tv70fTGkXWGAueDp5kAwHwYDVR0jBBgwFoAUmTeILVuBtv70fTGkXWGAueDp5kAw
|
||||
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAyVtq9XAvW7nxSW/8
|
||||
hp30z6xbzGiuviXhy/Jo91VEa8IRsWCCn3OmDFiVduTEowx76tf8clJP0gk7Pozi
|
||||
6dg/7Fin+FqQGXfCk8bLAh9gXKAikQ2GK8yRN3slRFwYC2mm23HrLdKXZHUqJcpB
|
||||
Mz2zsSrOGPj1YsYOl/U8FU6KA7Yj7U3q7kDMYTAgzUPZAH+d1DISGWpZsMa0RYid
|
||||
vigCCLByiccmS/Co4Sb1esF58H+YtV5+nFBRwx881U2g2TgDKF1lPMK/y3d8B8mh
|
||||
UtW+lFxRpvyNUDpsMjOErOrtNFEYbgoUJLtqwBMmyGR+nmmh6xna331QWcRAmw0P
|
||||
nDO4ew==
|
||||
-----END CERTIFICATE-----"""
|
||||
|
||||
@Throws(java.lang.Exception::class)
|
||||
fun generateFolderMetadataV2(userId: String, cert: String): DecryptedFolderMetadataFile {
|
||||
val metadata = DecryptedMetadata().apply {
|
||||
metadataKey = EncryptionUtils.generateKey()
|
||||
keyChecksums.add(EncryptionUtilsV2().hashMetadataKey(metadataKey))
|
||||
}
|
||||
|
||||
val file1 = DecryptedFile(
|
||||
"image1.png",
|
||||
"image/png",
|
||||
"gKm3n+mJzeY26q4OfuZEqg==",
|
||||
"PboI9tqHHX3QeAA22PIu4w==",
|
||||
"WANM0gRv+DhaexIsI0T3Lg=="
|
||||
)
|
||||
|
||||
val file2 = DecryptedFile(
|
||||
"image2.png",
|
||||
"image/png",
|
||||
"hnJLF8uhDvDoFK4ajuvwrg==",
|
||||
"qOQZdu5soFO77Y7y4rAOVA==",
|
||||
"9dfzbIYDt28zTyZfbcll+g=="
|
||||
)
|
||||
|
||||
val users = mutableListOf(
|
||||
DecryptedUser(userId, cert)
|
||||
)
|
||||
|
||||
// val filedrop = mutableMapOf(
|
||||
// Pair(
|
||||
// "eie8iaeiaes8e87td6",
|
||||
// DecryptedFile(
|
||||
// "test2.txt",
|
||||
// "txt/plain",
|
||||
// "hnJLF8uhDvDoFK4ajuvwrg==",
|
||||
// "qOQZdu5soFO77Y7y4rAOVA==",
|
||||
// "9dfzbIYDt28zTyZfbcll+g=="
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
|
||||
metadata.files["ia7OEEEyXMoRa1QWQk8r"] = file1
|
||||
metadata.files["n9WXAIXO2wRY4R8nXwmo"] = file2
|
||||
|
||||
return DecryptedFolderMetadataFile(metadata, users, mutableMapOf(), E2EVersion.V2_0.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.owncloud.android.EncryptionIT
|
||||
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedMetadata
|
||||
import com.owncloud.android.lib.resources.e2ee.CsrHelper
|
||||
import com.owncloud.android.operations.RefreshFolderOperation
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class EncryptionUtilsIT : EncryptionIT() {
|
||||
@Throws(
|
||||
java.security.NoSuchAlgorithmException::class,
|
||||
java.io.IOException::class,
|
||||
org.bouncycastle.operator.OperatorCreationException::class
|
||||
)
|
||||
@Test
|
||||
fun saveAndRestorePublicKey() {
|
||||
val arbitraryDataProvider = ArbitraryDataProviderImpl(targetContext)
|
||||
val keyPair = EncryptionUtils.generateKeyPair()
|
||||
val e2eUser = "e2e-user"
|
||||
val key = CsrHelper().generateCsrPemEncodedString(keyPair, e2eUser)
|
||||
|
||||
EncryptionUtils.savePublicKey(user, key, e2eUser, arbitraryDataProvider)
|
||||
|
||||
assertEquals(key, EncryptionUtils.getPublicKey(user, e2eUser, arbitraryDataProvider))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testUpdateFileNameForEncryptedFileV1() {
|
||||
val folder = testFolder()
|
||||
|
||||
val decryptedFilename = "image.png"
|
||||
val mockEncryptedFilename = "encrypted_file_name.png"
|
||||
|
||||
val decryptedMetadata = DecryptedMetadata()
|
||||
val filesData = DecryptedFile().apply {
|
||||
encrypted = Data().apply {
|
||||
filename = decryptedFilename
|
||||
}
|
||||
}
|
||||
val files = mapOf(mockEncryptedFilename to filesData)
|
||||
val metadata = DecryptedFolderMetadataFileV1(decryptedMetadata, files)
|
||||
|
||||
RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, metadata, folder)
|
||||
assertEquals(folder.decryptedRemotePath.contains("null"), false)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,913 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.nextcloud.client.account.MockUser
|
||||
import com.nextcloud.common.User
|
||||
import com.owncloud.android.EncryptionIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
|
||||
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFile
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedMetadata
|
||||
import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser
|
||||
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledrop
|
||||
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledropUser
|
||||
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile
|
||||
import com.owncloud.android.operations.RefreshFolderOperation
|
||||
import com.owncloud.android.util.EncryptionTestIT
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Test
|
||||
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
class EncryptionUtilsV2IT : EncryptionIT() {
|
||||
private val encryptionTestUtils = EncryptionTestUtils()
|
||||
private val encryptionUtilsV2 = EncryptionUtilsV2()
|
||||
|
||||
private val enc1UserId = "enc1"
|
||||
private val enc1PrivateKey = """
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAo
|
||||
IBAQDsn0JKS/THu328z1IgN0VzYU53HjSX03WJIgWkmyTaxbiKpoJaKbksXmfSpgzV
|
||||
GzKFvGfZ03fwFrN7Q8P8R2e8SNiell7mh1TDw9/0P7Bt/ER8PJrXORo+GviKHxaLr7
|
||||
Y0BJX9i/nW/L0L/VaE8CZTAqYBdcSJGgHJjY4UMf892ZPTa9T2Dl3ggdMZ7BQ2kiCi
|
||||
CC3qV99b0igRJGmmLQaGiAflhFzuDQPMifUMq75wI8RSRPdxUAtjTfkl68QHu7Umye
|
||||
yy33OQgdUKaTl5zcS3VSQbNjveVCNM4RDH1RlEc+7Wf1BY8APqT6jbiBcROJD2CeoL
|
||||
H2eiIJCi+61ZkSGfAgMBAAECggEBALFStCHrhBf+GL9a+qer4/8QZ/X6i91PmaBX/7
|
||||
SYk2jjjWVSXRNmex+V6+Y/jBRT2mvAgm8J+7LPwFdatE+lz0aZrMRD2gCWYF6Itpda
|
||||
90OlLkmQPVWWtGTgX2ta2tF5r2iSGzk0IdoL8zw98Q2UzpOcw30KnWtFMxuxWk0mHq
|
||||
pgp00g80cDWg3+RPbWOhdLp5bflQ36fKDfmjq05cGlIk6unnVyC5HXpvh4d4k2EWlX
|
||||
rjGsndVBPCjGkZePlLRgDHxT06r+5XdJ+1CBDZgCsmjGz3M8uOHyCfVW0WhB7ynzDT
|
||||
agVgz0iqpuhAi9sPt6iWWwpAnRw8cQgqEKw9bvKKECgYEA/WPi2PJtL6u/xlysh/H7
|
||||
A717CId6fPHCMDace39ZNtzUzc0nT5BemlcF0wZ74NeJSur3Q395YzB+eBMLs5p8mA
|
||||
95wgGvJhM65/J+HX+k9kt6Z556zLMvtG+j1yo4D0VEwm3xahB4SUUP+1kD7dNvo4+8
|
||||
xeSCyjzNllvYZZC0DrECgYEA7w8pEqhHHn0a+twkPCZJS+gQTB9Rm+FBNGJqB3XpWs
|
||||
TeLUxYRbVGk0iDve+eeeZ41drxcdyWP+WcL34hnrjgI1Fo4mK88saajpwUIYMy6+qM
|
||||
LY+jC2NRSBox56eH7nsVYvQQK9eKqv9wbB+PF9SwOIvuETN7fd8mAY02UnoaaU8CgY
|
||||
BoHRKocXPLkpZJuuppMVQiRUi4SHJbxDo19Tp2w+y0TihiJ1lvp7I3WGpcOt3LlMQk
|
||||
tEbExSvrRZGxZKH6Og/XqwQsYuTEkEIz679F/5yYVosE6GkskrOXQAfh8Mb3/04xVV
|
||||
tMaVgDQw0+CWVD4wyL+BNofGwBDNqsXTCdCsfxAQKBgQCDv2EtbRw0y1HRKv21QIxo
|
||||
ju5cZW4+cDfVPN+eWPdQFOs1H7wOPsc0aGRiiupV2BSEF3O1ApKziEE5U1QH+29bR4
|
||||
R8L1pemeGX8qCNj5bCubKjcWOz5PpouDcEqimZ3q98p3E6GEHN15UHoaTkx0yO/V8o
|
||||
j6zhQ9fYRxDHB5ACtQKBgQCOO7TJUO1IaLTjcrwS4oCfJyRnAdz49L1AbVJkIBK0fh
|
||||
JLecOFu3ZlQl/RStQb69QKb5MNOIMmQhg8WOxZxHcpmIDbkDAm/J/ovJXFSoBdOr5o
|
||||
uQsYsDZhsWW97zvLMzg5pH9/3/1BNz5q3Vu4HgfBSwWGt4E2NENj+XA+QAVmGA==
|
||||
""".trimIndent()
|
||||
|
||||
private val enc1Cert = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDpzCCAo+gAwIBAgIBADANBgkqhkiG9w0BAQUFADBuMRowGAYDVQQDDBF3d3cu
|
||||
bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0
|
||||
dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw
|
||||
HhcNMTcwOTI2MTAwNDMwWhcNMzcwOTIxMTAwNDMwWjBuMRowGAYDVQQDDBF3d3cu
|
||||
bmV4dGNsb3VkLmNvbTESMBAGA1UECgwJTmV4dGNsb3VkMRIwEAYDVQQHDAlTdHV0
|
||||
dGdhcnQxGzAZBgNVBAgMEkJhZGVuLVd1ZXJ0dGVtYmVyZzELMAkGA1UEBhMCREUw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsn0JKS/THu328z1IgN0Vz
|
||||
YU53HjSX03WJIgWkmyTaxbiKpoJaKbksXmfSpgzVGzKFvGfZ03fwFrN7Q8P8R2e8
|
||||
SNiell7mh1TDw9/0P7Bt/ER8PJrXORo+GviKHxaLr7Y0BJX9i/nW/L0L/VaE8CZT
|
||||
AqYBdcSJGgHJjY4UMf892ZPTa9T2Dl3ggdMZ7BQ2kiCiCC3qV99b0igRJGmmLQaG
|
||||
iAflhFzuDQPMifUMq75wI8RSRPdxUAtjTfkl68QHu7Umyeyy33OQgdUKaTl5zcS3
|
||||
VSQbNjveVCNM4RDH1RlEc+7Wf1BY8APqT6jbiBcROJD2CeoLH2eiIJCi+61ZkSGf
|
||||
AgMBAAGjUDBOMB0GA1UdDgQWBBTFrXz2tk1HivD9rQ75qeoyHrAgIjAfBgNVHSME
|
||||
GDAWgBTFrXz2tk1HivD9rQ75qeoyHrAgIjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
|
||||
DQEBBQUAA4IBAQARQTX21QKO77gAzBszFJ6xVnjfa23YZF26Z4X1KaM8uV8TGzuN
|
||||
JA95XmReeP2iO3r8EWXS9djVCD64m2xx6FOsrUI8HZaw1JErU8mmOaLAe8q9RsOm
|
||||
9Eq37e4vFp2YUEInYUqs87ByUcA4/8g3lEYeIUnRsRsWsA45S3wD7wy07t+KAn7j
|
||||
yMmfxdma6hFfG9iN/egN6QXUAyIPXvUvlUuZ7/BhWBj/3sHMrF9quy9Q2DOI8F3t
|
||||
1wdQrkq4BtStKhciY5AIXz9SqsctFHTv4Lwgtkapoel4izJnO0ZqYTXVe7THwri9
|
||||
H/gua6uJDWH9jk2/CiZDWfsyFuNUuXvDSp05
|
||||
-----END CERTIFICATE-----
|
||||
""".trimIndent()
|
||||
|
||||
private val enc2Cert = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC7DCCAdSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDDARlbmMz
|
||||
MB4XDTIwMDcwODA3MzE1OFoXDTQwMDcwMzA3MzE1OFowDzENMAsGA1UEAwwEZW5j
|
||||
MzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI/83eC/EF3xOocwjO+Z
|
||||
ZkPc1TFxt3aUgjEvrpZu45LOqesG67kkkVDYgjeg3Biz9XRUQXqtXaAyxRZH8GiH
|
||||
PFyKUiP1bUlCptd8X+hk9vxeN25YS5OS2RrxU9tDQ/dVOHr20427UvVCighotQnR
|
||||
/6+md1FQMV92PFxji7OP5TWOE1y389X6eb7kSPLs8Tu+2PpqaNVQ9C/89Y8KNYWs
|
||||
x9Zo+kbQhjfFFUikEpkuzMgT9QLaeq6xuXIPP+y1tzNmF6NTL0a2GoYULuxYWnCe
|
||||
joFyXj77LuLmK+KXfPdhvlxa5Kl9XHSxKPHBVVQpwPqNMT+b2T1VLE2l7M9NfImy
|
||||
iLcCAwEAAaNTMFEwHQYDVR0OBBYEFBKubDeR2lXwuyTrdyv6O7euPS4PMB8GA1Ud
|
||||
IwQYMBaAFBKubDeR2lXwuyTrdyv6O7euPS4PMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
|
||||
KoZIhvcNAQEFBQADggEBAChCOIH8CkEpm1eqjsuuNPa93aduLjtnZXat5eIKsKCl
|
||||
rL9nFslpg/DO5SeU5ynPY9F2QjX5CN/3RxDXum9vFfpXhTJphOv8N0uHU4ucmQxE
|
||||
DN388Vt5VtN3V2pzNUL3JSiG6qeYG047/r/zhGFVpcgb2465G5mEwFT0qnkEseCC
|
||||
VVZ63GN8hZgUobyRXxMIhkfWlbO1dgABB4VNyudq0CW8urmewkkbUBwCslvtUvPM
|
||||
WuzpQjq2A80bvbrAqO5VUfvMcqRiUWkDgfa6cHXyV0o4N11mMIoxsMgh+PFYr6lR
|
||||
BHkuQHqKEwP8kkWugIFj3TMcy9dYtXfMXWvzFaDoE4s=
|
||||
-----END CERTIFICATE-----
|
||||
""".trimIndent()
|
||||
|
||||
private val enc2PrivateKey = """
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCP/N3gvxBd8TqH
|
||||
MIzvmWZD3NUxcbd2lIIxL66WbuOSzqnrBuu5JJFQ2II3oNwYs/V0VEF6rV2gMsUW
|
||||
R/BohzxcilIj9W1JQqbXfF/oZPb8XjduWEuTktka8VPbQ0P3VTh69tONu1L1QooI
|
||||
aLUJ0f+vpndRUDFfdjxcY4uzj+U1jhNct/PV+nm+5Ejy7PE7vtj6amjVUPQv/PWP
|
||||
CjWFrMfWaPpG0IY3xRVIpBKZLszIE/UC2nqusblyDz/stbczZhejUy9GthqGFC7s
|
||||
WFpwno6Bcl4++y7i5ivil3z3Yb5cWuSpfVx0sSjxwVVUKcD6jTE/m9k9VSxNpezP
|
||||
TXyJsoi3AgMBAAECggEACWwKFtlZ2FPfORZ3unwGwZ0TRFOFJljMdiyBF6307Vfh
|
||||
rZP729clPS2Vw88eZ+1qu+yBhmYO0NtRo0Yc2LI0xHd2rYyzVI5sfYBRhFMLCHOf
|
||||
2/QiKet7knRFQP1TVr14Xy+Eo2slIBB1GNzFL/nSaeuSNjtxp6YEiCUpcJwTayAi
|
||||
Squ5QWMxhlciLKvwUkraFRBqkugvMz3jXzuk/i+DcYlOgoj+tytweNn/azOMH9MH
|
||||
mWI+3owYspjzE1rVpbrcWImvlnbInd0z9KaQPpBf7Njj7wtyBMaYww4K4GCMhboD
|
||||
SQCYgpnznWkPIN3jyXtmNVSsZ1nvD+Laod+0p7giOQKBgQDA6KEKctYpbt051yTe
|
||||
2UP8hpq+MUSS7FIXiHlUc8s0PSujouypUyzfrPeL6yquI0GtKHkMVCWwfT+otMZR
|
||||
VnklofrmPTPovvsUQFM4Di411NZwzfxEbBFyVXAUWcLd9NxJ1hZW7w+hLk/N5Bej
|
||||
DOa2CncZmifyMNIlvIn7T1vDyQKBgQC/FE8HaDBoN98m/3rEjx7/rVtX8dCei5By
|
||||
Fzg/yQ2u4ELbf/Qk/n4k75sy0690EwnFdJxVn2gdNgS1YDv8YP/N5Wfq8xnX9V9B
|
||||
irWY/W24cN2qDNXm5i8o5wklyt+fDVqMcEHFfONUpLC+RYmOdc1rrFxPaQOYYYpp
|
||||
dWsnuG0ofwKBgBm6rUf8ew35qG3/gP5sEgJLXbZCUfgapvRWkoAuFYs5IWno4BHR
|
||||
cym+IyI5Um75atgSjtqTGpfIjMYOnmjY1L2tNg6hWRwQ5OIVlkPiuE0bvyI6hwwF
|
||||
MeqC9LjyI+iAsSTz9fTQW9BOofw/ENwBa4AaMzpp8iv+UPkRhYHMWtvpAoGAX6As
|
||||
RMqxnxaHCR9GM2Rk4RPC6OpNu2qhKVfRgKp/vIrjKrKIXpM2UgnPo8oovnBgrX7E
|
||||
Vl1mX2gPRy4YFx/8JPCv5vcucdOMjmJ6q0v5QxrI9DdkPR/pbhDhlRZIf3LRZAMy
|
||||
B0GPC2c4RKDMTI1L9pzVvbASaoo2GLz4mXJEvsUCgYEAibwFNXz1H52sZtL6/1zQ
|
||||
1rHCTS8qkryBhxl5eYa6MV5YkbLJZZstF0w2nLxkPba8NttS/nJqjX/iJobD5uLb
|
||||
UzeD8jMeAWPNt4DZCtA4ossNYcXIMKqBVFKOANMvAAvLMpVdlNYSucNnTSQcLwI6
|
||||
2J9mW5WvAAaG+j28Q/GKSuE=
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun testEncryptDecryptMetadata() {
|
||||
val metadataKey = EncryptionUtils.generateKey()
|
||||
|
||||
val metadata = DecryptedMetadata(
|
||||
mutableListOf("hash1", "hash of key 2"),
|
||||
false,
|
||||
1,
|
||||
mutableMapOf(
|
||||
Pair(EncryptionUtils.generateUid(), "Folder 1"),
|
||||
Pair(EncryptionUtils.generateUid(), "Folder 2"),
|
||||
Pair(EncryptionUtils.generateUid(), "Folder 3")
|
||||
),
|
||||
mutableMapOf(
|
||||
Pair(
|
||||
EncryptionUtils.generateUid(),
|
||||
DecryptedFile(
|
||||
"file 1.png",
|
||||
"image/png",
|
||||
"initializationVector",
|
||||
"authenticationTag",
|
||||
"key 1"
|
||||
)
|
||||
),
|
||||
Pair(
|
||||
EncryptionUtils.generateUid(),
|
||||
DecryptedFile(
|
||||
"file 2.png",
|
||||
"image/png",
|
||||
"initializationVector 2",
|
||||
"authenticationTag 2",
|
||||
"key 2"
|
||||
)
|
||||
)
|
||||
),
|
||||
metadataKey
|
||||
)
|
||||
val encrypted = encryptionUtilsV2.encryptMetadata(metadata, metadataKey)
|
||||
val decrypted = encryptionUtilsV2.decryptMetadata(encrypted, metadataKey)
|
||||
|
||||
assertEquals(metadata, decrypted)
|
||||
}
|
||||
|
||||
@Throws(Throwable::class)
|
||||
@Test
|
||||
fun encryptDecryptSymmetric() {
|
||||
val string = "123"
|
||||
val metadataKey = EncryptionUtils.generateKeyString()
|
||||
|
||||
val e = EncryptionUtils.encryptStringSymmetricAsString(
|
||||
string,
|
||||
metadataKey.toByteArray()
|
||||
)
|
||||
|
||||
val d = EncryptionUtils.decryptStringSymmetric(e, metadataKey.toByteArray())
|
||||
assertEquals(string, d)
|
||||
|
||||
val encryptedMetadata = EncryptionUtils.encryptStringSymmetric(
|
||||
string,
|
||||
metadataKey.toByteArray(),
|
||||
EncryptionUtils.ivDelimiter
|
||||
)
|
||||
|
||||
val d2 = EncryptionUtils.decryptStringSymmetric(
|
||||
encryptedMetadata.ciphertext,
|
||||
metadataKey.toByteArray()
|
||||
)
|
||||
assertEquals(string, d2)
|
||||
|
||||
val decrypted = EncryptionUtils.decryptStringSymmetric(
|
||||
encryptedMetadata.ciphertext,
|
||||
metadataKey.toByteArray(),
|
||||
encryptedMetadata.authenticationTag,
|
||||
encryptedMetadata.nonce
|
||||
)
|
||||
|
||||
assertEquals(string, EncryptionUtils.decodeBase64BytesToString(decrypted))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEncryptDecryptUser() {
|
||||
val metadataKeyBase64 = EncryptionUtils.generateKeyString()
|
||||
val metadataKey = EncryptionUtils.decodeStringToBase64Bytes(metadataKeyBase64)
|
||||
|
||||
val user = DecryptedUser("t1", encryptionTestUtils.t1PublicKey)
|
||||
|
||||
val encryptedUser = encryptionUtilsV2.encryptUser(user, metadataKey)
|
||||
assertNotEquals(encryptedUser.encryptedMetadataKey, metadataKeyBase64)
|
||||
|
||||
val decryptedMetadataKey = encryptionUtilsV2.decryptMetadataKey(encryptedUser, encryptionTestUtils.t1PrivateKey)
|
||||
val decryptedMetadataKeyBase64 = EncryptionUtils.encodeBytesToBase64String(decryptedMetadataKey)
|
||||
|
||||
assertEquals(metadataKeyBase64, decryptedMetadataKeyBase64)
|
||||
}
|
||||
|
||||
@Throws(com.owncloud.android.operations.UploadException::class, Throwable::class)
|
||||
@Test
|
||||
fun testEncryptDecryptMetadataFile() {
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
|
||||
val root = OCFile("/")
|
||||
storageManager.saveFile(root)
|
||||
|
||||
val folder = OCFile("/enc/").apply {
|
||||
parentId = storageManager.getFileByDecryptedRemotePath("/")?.fileId ?: throw IllegalStateException()
|
||||
}
|
||||
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
|
||||
val encrypted = encryptionUtilsV2.encryptFolderMetadataFile(
|
||||
metadataFile,
|
||||
enc1.accountName,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
enc1PrivateKey,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
|
||||
val signature = encryptionUtilsV2.getMessageSignature(enc1Cert, enc1PrivateKey, encrypted)
|
||||
|
||||
val decrypted = encryptionUtilsV2.decryptFolderMetadataFile(
|
||||
encrypted,
|
||||
enc1.accountName,
|
||||
enc1PrivateKey,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
0,
|
||||
signature,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
|
||||
assertEquals(metadataFile, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addFile() {
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
assertEquals(2, metadataFile.metadata.files.size)
|
||||
assertEquals(1, metadataFile.metadata.counter)
|
||||
|
||||
val updatedMetadata = encryptionUtilsV2.addFileToMetadata(
|
||||
EncryptionUtils.generateUid(),
|
||||
OCFile("/test.jpg").apply {
|
||||
mimeType = MimeType.JPEG
|
||||
},
|
||||
EncryptionUtils.generateIV(),
|
||||
EncryptionUtils.generateUid(), // random string, not real tag
|
||||
EncryptionUtils.generateKey(),
|
||||
metadataFile,
|
||||
storageManager
|
||||
)
|
||||
|
||||
assertEquals(3, updatedMetadata.metadata.files.size)
|
||||
assertEquals(2, updatedMetadata.metadata.counter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeFile() {
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
assertEquals(2, metadataFile.metadata.files.size)
|
||||
|
||||
val filename = metadataFile.metadata.files.keys.first()
|
||||
|
||||
encryptionUtilsV2.removeFileFromMetadata(filename, metadataFile)
|
||||
|
||||
assertEquals(1, metadataFile.metadata.files.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renameFile() {
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
assertEquals(2, metadataFile.metadata.files.size)
|
||||
|
||||
val key = metadataFile.metadata.files.keys.first()
|
||||
val decryptedFile = metadataFile.metadata.files[key]
|
||||
val filename = decryptedFile?.filename
|
||||
val newFilename = "New File 1"
|
||||
|
||||
encryptionUtilsV2.renameFile(key, newFilename, metadataFile)
|
||||
|
||||
assertEquals(newFilename, metadataFile.metadata.files[key]?.filename)
|
||||
assertNotEquals(filename, newFilename)
|
||||
assertNotEquals(filename, metadataFile.metadata.files[key]?.filename)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addFolder() {
|
||||
val folder = OCFile("/e/")
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
assertEquals(2, metadataFile.metadata.files.size)
|
||||
assertEquals(3, metadataFile.metadata.folders.size)
|
||||
|
||||
val updatedMetadata = encryptionUtilsV2.addFolderToMetadata(
|
||||
EncryptionUtils.generateUid(),
|
||||
"new subfolder",
|
||||
metadataFile,
|
||||
folder,
|
||||
storageManager
|
||||
)
|
||||
|
||||
assertEquals(2, updatedMetadata.metadata.files.size)
|
||||
assertEquals(4, updatedMetadata.metadata.folders.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeFolder() {
|
||||
val folder = OCFile("/e/")
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
assertEquals(2, metadataFile.metadata.files.size)
|
||||
assertEquals(3, metadataFile.metadata.folders.size)
|
||||
|
||||
val encryptedFileName = EncryptionUtils.generateUid()
|
||||
var updatedMetadata = encryptionUtilsV2.addFolderToMetadata(
|
||||
encryptedFileName,
|
||||
"new subfolder",
|
||||
metadataFile,
|
||||
folder,
|
||||
storageManager
|
||||
)
|
||||
|
||||
assertEquals(2, updatedMetadata.metadata.files.size)
|
||||
assertEquals(4, updatedMetadata.metadata.folders.size)
|
||||
|
||||
updatedMetadata = encryptionUtilsV2.removeFolderFromMetadata(
|
||||
encryptedFileName,
|
||||
updatedMetadata
|
||||
)
|
||||
|
||||
assertEquals(2, updatedMetadata.metadata.files.size)
|
||||
assertEquals(3, updatedMetadata.metadata.folders.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyMetadata() {
|
||||
val folder = OCFile("/e/")
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
val encrypted = encryptionUtilsV2.encryptFolderMetadataFile(
|
||||
metadataFile,
|
||||
enc1UserId,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
enc1PrivateKey,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
|
||||
val signature = encryptionUtilsV2.getMessageSignature(enc1Cert, enc1PrivateKey, encrypted)
|
||||
|
||||
encryptionUtilsV2.verifyMetadata(encrypted, metadataFile, 0, signature)
|
||||
|
||||
assertTrue(true) // if we reach this, test is successful
|
||||
}
|
||||
|
||||
private fun generateDecryptedFileV1(): com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile {
|
||||
return com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile().apply {
|
||||
encrypted = Data().apply {
|
||||
key = EncryptionUtils.generateKeyString()
|
||||
filename = "Random filename.jpg"
|
||||
mimetype = MimeType.JPEG
|
||||
version = 1.0
|
||||
}
|
||||
initializationVector = EncryptionUtils.generateKeyString()
|
||||
authenticationTag = EncryptionUtils.generateKeyString()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMigrateDecryptedV1ToV2() {
|
||||
val v1 = generateDecryptedFileV1()
|
||||
val v2 = encryptionUtilsV2.migrateDecryptedFileV1ToV2(v1)
|
||||
|
||||
assertEquals(v1.encrypted.filename, v2.filename)
|
||||
assertEquals(v1.encrypted.mimetype, v2.mimetype)
|
||||
assertEquals(v1.authenticationTag, v2.authenticationTag)
|
||||
assertEquals(v1.initializationVector, v2.nonce)
|
||||
assertEquals(v1.encrypted.key, v2.key)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMigrateMetadataV1ToV2() {
|
||||
OCFile("/").apply {
|
||||
storageManager.saveFile(this)
|
||||
}
|
||||
|
||||
val folder = OCFile("/enc/").apply {
|
||||
parentId = storageManager.getFileByDecryptedRemotePath("/")?.fileId ?: throw IllegalStateException()
|
||||
}
|
||||
|
||||
val v1 = DecryptedFolderMetadataFileV1().apply {
|
||||
metadata = com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedMetadata().apply {
|
||||
metadataKeys = mapOf(Pair(0, EncryptionUtils.generateKeyString()))
|
||||
}
|
||||
files = mapOf(
|
||||
Pair(EncryptionUtils.generateUid(), generateDecryptedFileV1()),
|
||||
Pair(EncryptionUtils.generateUid(), generateDecryptedFileV1()),
|
||||
Pair(
|
||||
EncryptionUtils.generateUid(),
|
||||
com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile().apply {
|
||||
encrypted = Data().apply {
|
||||
key = EncryptionUtils.generateKeyString()
|
||||
filename = "subFolder"
|
||||
mimetype = MimeType.WEBDAV_FOLDER
|
||||
}
|
||||
initializationVector = EncryptionUtils.generateKeyString()
|
||||
authenticationTag = null
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
val v2 = encryptionUtilsV2.migrateV1ToV2(
|
||||
v1,
|
||||
enc1UserId,
|
||||
enc1Cert,
|
||||
folder,
|
||||
storageManager
|
||||
)
|
||||
|
||||
assertEquals(2, v2.metadata.files.size)
|
||||
assertEquals(1, v2.metadata.folders.size)
|
||||
assertEquals(1, v2.users.size) // only one user upon migration
|
||||
}
|
||||
|
||||
@Throws(com.owncloud.android.operations.UploadException::class, Throwable::class)
|
||||
@Test
|
||||
fun addSharee() {
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val enc2 = MockUser("enc2", "Nextcloud")
|
||||
|
||||
val root = OCFile("/")
|
||||
storageManager.saveFile(root)
|
||||
|
||||
val folder = OCFile("/enc/").apply {
|
||||
parentId = storageManager.getFileByDecryptedRemotePath("/")?.fileId ?: throw IllegalStateException()
|
||||
}
|
||||
|
||||
var metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
|
||||
metadataFile = encryptionUtilsV2.addShareeToMetadata(metadataFile, enc2.accountName, enc2Cert)
|
||||
|
||||
val encryptedMetadataFile = encryptionUtilsV2.encryptFolderMetadataFile(
|
||||
metadataFile,
|
||||
client.userId,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
enc1PrivateKey,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
|
||||
val signature = encryptionUtilsV2.getMessageSignature(enc1Cert, enc1PrivateKey, encryptedMetadataFile)
|
||||
|
||||
val decryptedByEnc1 = encryptionUtilsV2.decryptFolderMetadataFile(
|
||||
encryptedMetadataFile,
|
||||
enc1.accountName,
|
||||
enc1PrivateKey,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
metadataFile.metadata.counter,
|
||||
signature,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
assertEquals(metadataFile.metadata, decryptedByEnc1.metadata)
|
||||
|
||||
val decryptedByEnc2 = encryptionUtilsV2.decryptFolderMetadataFile(
|
||||
encryptedMetadataFile,
|
||||
enc2.accountName,
|
||||
enc2PrivateKey,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
metadataFile.metadata.counter,
|
||||
signature,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
assertEquals(metadataFile.metadata, decryptedByEnc2.metadata)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeSharee() {
|
||||
val enc1 = MockUser("enc1", "Nextcloud")
|
||||
val enc2 = MockUser("enc2", "Nextcloud")
|
||||
var metadataFile = generateDecryptedFolderMetadataFile(enc1, enc1Cert)
|
||||
metadataFile = encryptionUtilsV2.addShareeToMetadata(metadataFile, enc2.accountName, enc2Cert)
|
||||
|
||||
assertEquals(2, metadataFile.users.size)
|
||||
|
||||
metadataFile = encryptionUtilsV2.removeShareeFromMetadata(metadataFile, enc2.accountName)
|
||||
|
||||
assertEquals(1, metadataFile.users.size)
|
||||
}
|
||||
|
||||
private fun generateDecryptedFolderMetadataFile(user: User, cert: String): DecryptedFolderMetadataFile {
|
||||
val metadata = DecryptedMetadata(
|
||||
mutableListOf("hash1", "hash of key 2"),
|
||||
false,
|
||||
1,
|
||||
mutableMapOf(
|
||||
Pair(EncryptionUtils.generateUid(), "Folder 1"),
|
||||
Pair(EncryptionUtils.generateUid(), "Folder 2"),
|
||||
Pair(EncryptionUtils.generateUid(), "Folder 3")
|
||||
),
|
||||
mutableMapOf(
|
||||
Pair(
|
||||
EncryptionUtils.generateUid(),
|
||||
DecryptedFile(
|
||||
"file 1.png",
|
||||
"image/png",
|
||||
"initializationVector",
|
||||
"authenticationTag",
|
||||
"key 1"
|
||||
)
|
||||
),
|
||||
Pair(
|
||||
EncryptionUtils.generateUid(),
|
||||
DecryptedFile(
|
||||
"file 2.png",
|
||||
"image/png",
|
||||
"initializationVector 2",
|
||||
"authenticationTag 2",
|
||||
"key 2"
|
||||
)
|
||||
)
|
||||
),
|
||||
EncryptionUtils.generateKey()
|
||||
)
|
||||
|
||||
val users = mutableListOf(
|
||||
DecryptedUser(user.accountName, cert)
|
||||
)
|
||||
|
||||
metadata.keyChecksums.add(encryptionUtilsV2.hashMetadataKey(metadata.metadataKey))
|
||||
|
||||
return DecryptedFolderMetadataFile(metadata, users, mutableMapOf())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGZip() {
|
||||
val string = """
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
This is a test.
|
||||
It contains linewraps and special characters:
|
||||
$$|²›³¥!’‘‘
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
val gzipped = encryptionUtilsV2.gZipCompress(string)
|
||||
|
||||
val result = encryptionUtilsV2.gZipDecompress(gzipped)
|
||||
|
||||
assertEquals(string, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun gunzip() {
|
||||
val string = "H4sICNVkD2QAAwArycgsVgCiRIWS1OISPQDD9wZODwAAAA=="
|
||||
val decoded = EncryptionUtils.decodeStringToBase64Bytes(string)
|
||||
val gunzip = encryptionUtilsV2.gZipDecompress(decoded)
|
||||
|
||||
assertEquals("this is a test.\n", gunzip)
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun validate() {
|
||||
// // ALEX
|
||||
// val metadata1 = """{
|
||||
// "metadata": {
|
||||
// "authenticationTag": "zMozev5R09UopLrq7Je1lw==",
|
||||
// "ciphertext": "j0OBtUrEt4IveGiexjmGK7eKEaWrY70ZkteA5KxHDaZT/t2wwGy9j2FPQGpqXnW6OO3iAYPNgwFikI1smnfNvqdxzVDvhavl/IXa9Kg2niWyqK3D9zpz0YD6mDvl0XsOgTNVyGXNVREdWgzGEERCQoyHI1xowt/swe3KCXw+lf+XPF/t1PfHv0DiDVk70AeWGpPPPu6yggAIxB4Az6PEZhaQWweTC0an48l2FHj2MtB2PiMHtW2v7RMuE8Al3PtE4gOA8CMFrB+Npy6rKcFCXOgTZm5bp7q+J1qkhBDbiBYtvdsYujJ52Xa5SifTpEhGeWWLFnLLgPAQ8o6bXcWOyCoYfLfp4Jpft/Y7H8qzHbPewNSyD6maEv+xljjfU7hxibbszz5A4JjMdQy2BDGoTmJx7Mas+g6l6ZuHLVbdmgQOvD3waJBy6rOg0euux0Cn4bB4bIFEF2KvbhdGbY1Uiq9DYa7kEmSEnlcAYaHyroTkDg4ew7ER0vIBBMzKM3r+UdPVKKS66uyXtZc=",
|
||||
// "nonce": "W+lxQJeGq7XAJiGfcDohkg=="
|
||||
// },
|
||||
// "users": [{
|
||||
// "certificate": "-----BEGIN CERTIFICATE-----\nMIIDkDCCAnigAwIBAgIBADANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJERTEb\nMBkGA1UECAwSQmFkZW4tV3VlcnR0ZW1iZXJnMRIwEAYDVQQHDAlTdHV0dGdhcnQx\nEjAQBgNVBAoMCU5leHRjbG91ZDENMAsGA1UEAwwEam9objAeFw0yMzA3MTQwNzM0\nNTZaFw00MzA3MDkwNzM0NTZaMGExCzAJBgNVBAYTAkRFMRswGQYDVQQIDBJCYWRl\nbi1XdWVydHRlbWJlcmcxEjAQBgNVBAcMCVN0dXR0Z2FydDESMBAGA1UECgwJTmV4\ndGNsb3VkMQ0wCwYDVQQDDARqb2huMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA7j3Er5YahJT0LAnSRLhpqbRo+E1AVnt98rvp3DmEfBHNzNB+DS9IBDkS\nSXM/YtfAci6Tcw8ujVBjrZX/WEmrf8ynQHxYmSaJSnP8uAT306/MceZpdpruEc9/\nS10a7vp54Zbld4NYdmfS71oVFVKgM7c/Vthx+rgu48fuxzbWAvVYLFcx47hz0DJT\nnjz2Za/R68uXpxfz7J9uEXYiqsAs/FobDsLZluT3RyywVRwKBed1EZxUeLIJiyxp\nUthhGfIb8b3Vf9jZoUVi3m5gmc4spJQHvYAkfZYHzd9ras8jBu1abQRxcu2CYnVo\n6Y0mTxhKhQS/n5gjv3ExiQF3wp/XYwIDAQABo1MwUTAdBgNVHQ4EFgQUmTeILVuB\ntv70fTGkXWGAueDp5kAwHwYDVR0jBBgwFoAUmTeILVuBtv70fTGkXWGAueDp5kAw\nDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAyVtq9XAvW7nxSW/8\nhp30z6xbzGiuviXhy/Jo91VEa8IRsWCCn3OmDFiVduTEowx76tf8clJP0gk7Pozi\n6dg/7Fin+FqQGXfCk8bLAh9gXKAikQ2GK8yRN3slRFwYC2mm23HrLdKXZHUqJcpB\nMz2zsSrOGPj1YsYOl/U8FU6KA7Yj7U3q7kDMYTAgzUPZAH+d1DISGWpZsMa0RYid\nvigCCLByiccmS/Co4Sb1esF58H+YtV5+nFBRwx881U2g2TgDKF1lPMK/y3d8B8mh\nUtW+lFxRpvyNUDpsMjOErOrtNFEYbgoUJLtqwBMmyGR+nmmh6xna331QWcRAmw0P\nnDO4ew==\n-----END CERTIFICATE-----\n",
|
||||
// "encryptedMetadataKey": "HVT49bYmwXbGs/dJ2avgU9unrKnPf03MYUI5ZysSR1Bz5pqz64gzH2GBAuUJ+Q4VmHtEfcMaWW7VXgzfCQv5xLBrk+RSgcLOKnlIya8jaDlfttWxbe8jJK+/0+QVPOc6ycA/t5HNCPg09hzj+gnb2L89UHxL5accZD0iEzb5cQbGrc/N6GthjgGrgFKtFf0HhDVplUr+DL9aTyKuKLBPjrjuZbv8M6ZfXO93mOMwSZH3c3rwDUHb/KEaTR/Og4pWQmrqr1VxGLqeV/+GKWhzMYThrOZAUz+5gsbckU2M5V9i+ph0yBI5BjOZVhNuDwW8yP8WtyRJwQc+UBRei/RGBQ==",
|
||||
// "userId": "john"
|
||||
// }],
|
||||
// "version": "2"
|
||||
// }
|
||||
//
|
||||
// """
|
||||
//
|
||||
// val signature1 =
|
||||
// "ewogICAgIm1ldGFkYXRhIjogewogICAgICAgICJhdXRoZW50aWNhdGlvblRhZyI6ICJ6TW96ZXY1UjA5VW9wTHJxN0plMWx3PT0iLAogICAgICAgICJjaXBoZXJ0ZXh0IjogImowT0J0VXJFdDRJdmVHaWV4am1HSzdlS0VhV3JZNzBaa3RlQTVLeEhEYVpUL3Qyd3dHeTlqMkZQUUdwcVhuVzZPTzNpQVlQTmd3RmlrSTFzbW5mTnZxZHh6VkR2aGF2bC9JWGE5S2cybmlXeXFLM0Q5enB6MFlENm1EdmwwWHNPZ1ROVnlHWE5WUkVkV2d6R0VFUkNRb3lISTF4b3d0L3N3ZTNLQ1h3K2xmK1hQRi90MVBmSHYwRGlEVms3MEFlV0dwUFBQdTZ5Z2dBSXhCNEF6NlBFWmhhUVd3ZVRDMGFuNDhsMkZIajJNdEIyUGlNSHRXMnY3Uk11RThBbDNQdEU0Z09BOENNRnJCK05weTZyS2NGQ1hPZ1RabTVicDdxK0oxcWtoQkRiaUJZdHZkc1l1ako1MlhhNVNpZlRwRWhHZVdXTEZuTExnUEFROG82YlhjV095Q29ZZkxmcDRKcGZ0L1k3SDhxekhiUGV3TlN5RDZtYUV2K3hsampmVTdoeGliYnN6ejVBNEpqTWRReTJCREdvVG1KeDdNYXMrZzZsNlp1SExWYmRtZ1FPdkQzd2FKQnk2ck9nMGV1dXgwQ240YkI0YklGRUYyS3ZiaGRHYlkxVWlxOURZYTdrRW1TRW5sY0FZYUh5cm9Ua0RnNGV3N0VSMHZJQkJNektNM3IrVWRQVktLUzY2dXlYdFpjPSIsCiAgICAgICAgIm5vbmNlIjogIlcrbHhRSmVHcTdYQUppR2ZjRG9oa2c9PSIKICAgIH0sCiAgICAidXNlcnMiOiB7CiAgICAgICAgImNlcnRpZmljYXRlIjogIi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLVxuTUlJRGtEQ0NBbmlnQXdJQkFnSUJBREFOQmdrcWhraUc5dzBCQVFVRkFEQmhNUXN3Q1FZRFZRUUdFd0pFUlRFYlxuTUJrR0ExVUVDQXdTUW1Ga1pXNHRWM1ZsY25SMFpXMWlaWEpuTVJJd0VBWURWUVFIREFsVGRIVjBkR2RoY25ReFxuRWpBUUJnTlZCQW9NQ1U1bGVIUmpiRzkxWkRFTk1Bc0dBMVVFQXd3RWFtOW9iakFlRncweU16QTNNVFF3TnpNMFxuTlRaYUZ3MDBNekEzTURrd056TTBOVFphTUdFeEN6QUpCZ05WQkFZVEFrUkZNUnN3R1FZRFZRUUlEQkpDWVdSbFxuYmkxWGRXVnlkSFJsYldKbGNtY3hFakFRQmdOVkJBY01DVk4wZFhSMFoyRnlkREVTTUJBR0ExVUVDZ3dKVG1WNFxuZEdOc2IzVmtNUTB3Q3dZRFZRUUREQVJxYjJodU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQlxuQ2dLQ0FRRUE3ajNFcjVZYWhKVDBMQW5TUkxocHFiUm8rRTFBVm50OThydnAzRG1FZkJITnpOQitEUzlJQkRrU1xuU1hNL1l0ZkFjaTZUY3c4dWpWQmpyWlgvV0VtcmY4eW5RSHhZbVNhSlNuUDh1QVQzMDYvTWNlWnBkcHJ1RWM5L1xuUzEwYTd2cDU0WmJsZDROWWRtZlM3MW9WRlZLZ003Yy9WdGh4K3JndTQ4ZnV4emJXQXZWWUxGY3g0N2h6MERKVFxubmp6MlphL1I2OHVYcHhmejdKOXVFWFlpcXNBcy9Gb2JEc0xabHVUM1J5eXdWUndLQmVkMUVaeFVlTElKaXl4cFxuVXRoaEdmSWI4YjNWZjlqWm9VVmkzbTVnbWM0c3BKUUh2WUFrZlpZSHpkOXJhczhqQnUxYWJRUnhjdTJDWW5Wb1xuNlkwbVR4aEtoUVMvbjVnanYzRXhpUUYzd3AvWFl3SURBUUFCbzFNd1VUQWRCZ05WSFE0RUZnUVVtVGVJTFZ1QlxudHY3MGZUR2tYV0dBdWVEcDVrQXdId1lEVlIwakJCZ3dGb0FVbVRlSUxWdUJ0djcwZlRHa1hXR0F1ZURwNWtBd1xuRHdZRFZSMFRBUUgvQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCQVFVRkFBT0NBUUVBeVZ0cTlYQXZXN254U1cvOFxuaHAzMHo2eGJ6R2l1dmlYaHkvSm85MVZFYThJUnNXQ0NuM09tREZpVmR1VEVvd3g3NnRmOGNsSlAwZ2s3UG96aVxuNmRnLzdGaW4rRnFRR1hmQ2s4YkxBaDlnWEtBaWtRMkdLOHlSTjNzbFJGd1lDMm1tMjNIckxkS1haSFVxSmNwQlxuTXoyenNTck9HUGoxWXNZT2wvVThGVTZLQTdZajdVM3E3a0RNWVRBZ3pVUFpBSCtkMURJU0dXcFpzTWEwUllpZFxudmlnQ0NMQnlpY2NtUy9DbzRTYjFlc0Y1OEgrWXRWNStuRkJSd3g4ODFVMmcyVGdES0YxbFBNSy95M2Q4QjhtaFxuVXRXK2xGeFJwdnlOVURwc01qT0VyT3J0TkZFWWJnb1VKTHRxd0JNbXlHUitubW1oNnhuYTMzMVFXY1JBbXcwUFxubkRPNGV3PT1cbi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS1cbiIsCiAgICAgICAgImVuY3J5cHRlZE1ldGFkYXRhS2V5IjogIkhWVDQ5Ylltd1hiR3MvZEoyYXZnVTl1bnJLblBmMDNNWVVJNVp5c1NSMUJ6NXBxejY0Z3pIMkdCQXVVSitRNFZtSHRFZmNNYVdXN1ZYZ3pmQ1F2NXhMQnJrK1JTZ2NMT0tubEl5YThqYURsZnR0V3hiZThqSksrLzArUVZQT2M2eWNBL3Q1SE5DUGcwOWh6aitnbmIyTDg5VUh4TDVhY2NaRDBpRXpiNWNRYkdyYy9ONkd0aGpnR3JnRkt0RmYwSGhEVnBsVXIrREw5YVR5S3VLTEJQanJqdVpidjhNNlpmWE85M21PTXdTWkgzYzNyd0RVSGIvS0VhVFIvT2c0cFdRbXJxcjFWeEdMcWVWLytHS1doek1ZVGhyT1pBVXorNWdzYmNrVTJNNVY5aStwaDB5Qkk1QmpPWlZoTnVEd1c4eVA4V3R5Ukp3UWMrVUJSZWkvUkdCUT09IiwKICAgICAgICAidXNlcklkIjogImpvaG4iCiAgICB9LAogICAgInZlcnNpb24iOiAiMiIKfQo="
|
||||
//
|
||||
// // TOBI
|
||||
// val metadata =
|
||||
// """{"metadata":{"authenticationTag":"qDcJnAAGtGDlHWiQMBfXgw\u003d\u003d","ciphertext":"3zUhwIgJWMB7DvrbsDaMvh8MbJdoTxL0OMPCCdYSfBt7gB+V/hwqelL1IOaLto3avhHGSebnrotF06iEP/jZwWg9hApIPTHc8B4XTOY0/kezqYyVqTyquTUZpDpqgVAheQskZZ8I4Ir0seajUkt4KtVRfzO6v8CePRrEg6uKwdYsqDcJnAAGtGDlHWiQMBfXgw\u003d\u003d|4hbOyn1ykQL+9D6SnPY3cQ\u003d\u003d","nonce":"4hbOyn1ykQL+9D6SnPY3cQ\u003d\u003d"},"users":[{"certificate":"-----BEGIN CERTIFICATE-----\nMIIC6DCCAdCgAwIBAgIBADANBgkqhkiG9w0BAQUFADANMQswCQYDVQQDDAJ0MTAe\nFw0yMzA3MjUwNzU3MTJaFw00MzA3MjAwNzU3MTJaMA0xCzAJBgNVBAMMAnQxMIIB\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtafHmDBcBqIu4HmMxMDW3j0S\ny+S0YaKwHnBRt85KSwcEov0B5FOLuLknoBGx4Dn3u93ilThXXxacPMHeXL7WPuAs\n21/G7vsqwvrRRnCduf+FUO/AZeDCNErzpsQ8LmTa4PUloLPUcImpSjrHwhMs9Ekv\nEbLRjbeSmSp9XvM+1fV/3jkT5jkOSnCFx5TGwGN5uHqwUir4UWXasvg253NK2XmW\nipKCDCR9TmH1baP3pNdoiChdmErT1c6E4DbBXpTw8XgP5ZbYH+qg1UQ/hC8nRJ3D\nyCcHL+dg/GYraBMhDn4w2Vvq77xNNoNWQ9cT5Ay6cJbQLBQoJQirygQFrobYRQID\nAQABo1MwUTAdBgNVHQ4EFgQUE9zCeA9/QMAtVgLxD23X6ZcodhMwHwYDVR0jBBgw\nFoAUE9zCeA9/QMAtVgLxD23X6ZcodhMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG\n9w0BAQUFAAOCAQEAZdy/YjJlvnz3FQwxp6oVtMJccpdxveEPfLzgaverhtd/vP8O\nAvDzOLgQJHmrDS91SG503eU4cYGyuNKwd77OyTnqMg+GUEmJhGfPpSVrEIdh65jv\nq61T4oqBdehevVmBq54rGiwL0DGv1DlXQlwiJZP4qni2KnOEFcnvL3gVtRnQjXQ+\nkHvlMshkK6w021EMV5NfjG2zg67wC65rLaej5f6Ssp2S7g2VtmE4aXq1bjAuEbqk\n4TiyZHLDdsJuqzyGyyOpMV7i9ucXDoaZt9cGS9hT2vRxTrSH63vKR8Xeig9+stLw\nt9ONcUqCKP7hd8rajtxM4JIIRExwD8OkgARWGg\u003d\u003d\n-----END CERTIFICATE-----\n","encryptedMetadataKey":"s4kDkkLpk1mSmXedP7huiCNC4DYmDAmA2VYGem5M8jIGPC6miVQoo4WXZrEBhdsLw7Msf5iT3A3fTaHhwsI8Jf4McsFyM9/FXT1mCEaGOEpNjbKOlJY1uPUFNOhLqUfFiBos6oBT53hWwoXWjytYvLBbXuXY5YLOysjgBh6URrgFUZAJAmcOJ6OFKgfIIthoqkQc7CQUY97VsRzAXzeYTANBc2yW1pSN51HqftvMzvewFRsJQLcu7a9NjpTdG9LiLhn5eLXOLymXEE/aaPHKXeprlXLzrdWU1xwZRJqV+to2FEiH6CQNsO4+9h5m0VjXekiNeAFrsXB5cJgUipGuzQ\u003d\u003d","userId":"t1"}],"version":"2.0"}"""
|
||||
//
|
||||
// val base = EncryptionUtils.encodeStringToBase64String(metadata)
|
||||
//
|
||||
// val signature =
|
||||
// "MIAGCSqGSIb3DQEHAqCAMIACAQExDTALBglghkgBZQMEAgEwCwYJKoZIhvcNAQcBoIAwggLoMIIB0KADAgECAgEAMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAMMAnQxMB4XDTIzMDcyNTA3NTcxMloXDTQzMDcyMDA3NTcxMlowDTELMAkGA1UEAwwCdDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1p8eYMFwGoi7geYzEwNbePRLL5LRhorAecFG3zkpLBwSi/QHkU4u4uSegEbHgOfe73eKVOFdfFpw8wd5cvtY+4CzbX8bu+yrC+tFGcJ25/4VQ78Bl4MI0SvOmxDwuZNrg9SWgs9RwialKOsfCEyz0SS8RstGNt5KZKn1e8z7V9X/eORPmOQ5KcIXHlMbAY3m4erBSKvhRZdqy+Dbnc0rZeZaKkoIMJH1OYfVto/ek12iIKF2YStPVzoTgNsFelPDxeA/lltgf6qDVRD+ELydEncPIJwcv52D8ZitoEyEOfjDZW+rvvE02g1ZD1xPkDLpwltAsFCglCKvKBAWuhthFAgMBAAGjUzBRMB0GA1UdDgQWBBQT3MJ4D39AwC1WAvEPbdfplyh2EzAfBgNVHSMEGDAWgBQT3MJ4D39AwC1WAvEPbdfplyh2EzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBl3L9iMmW+fPcVDDGnqhW0wlxyl3G94Q98vOBq96uG13+8/w4C8PM4uBAkeasNL3VIbnTd5ThxgbK40rB3vs7JOeoyD4ZQSYmEZ8+lJWsQh2HrmO+rrVPiioF16F69WYGrnisaLAvQMa/UOVdCXCIlk/iqeLYqc4QVye8veBW1GdCNdD6Qe+UyyGQrrDTbUQxXk1+MbbODrvALrmstp6Pl/pKynZLuDZW2YThperVuMC4RuqThOLJkcsN2wm6rPIbLI6kxXuL25xcOhpm31wZL2FPa9HFOtIfre8pHxd6KD36y0vC3041xSoIo/uF3ytqO3EzgkghETHAPw6SABFYaAAAxggHUMIIB0AIBATASMA0xCzAJBgNVBAMMAnQxAgEAMAsGCWCGSAFlAwQCAaCBljAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMzA3MjgwNzMwMTJaMCsGCSqGSIb3DQEJNDEeMBwwCwYJYIZIAWUDBAIBoQ0GCSqGSIb3DQEBCwUAMC8GCSqGSIb3DQEJBDEiBCAx7RTJg7hbY5Mkzjw3f6qhX7k/J0FdVz2cL3ow0AmyYjANBgkqhkiG9w0BAQsFAASCAQAbUmb9e7eoIcPNzDSmnzbrueBzgT8YszNGEI+1YCq8XdWN4kDztvP1ZNV21VCO6BvcbfUAnXXgcX5BPeLZNsgXPj3c8TbD59GQl3oT/tIchgMsA20RdAtIwvItlZKh+X6sp0OHkRPYSk/mEYKCKPqrKdJicRWex8ItCwpDR91KSOiKJrN/+DKOGG0sVI9gjzbtrHsN8HmVKxOoNV+wwipcLsWsEmuV+wvPCQ9HJidLX9Q17Bgfc+qJg19aB6iKLWPhjgnfpKGbK5VJuQTdDWPUJ2O4G3W/iwxJ0hAJ7tks4zIATmgGzhgTWYx5LVXbKcuL04xhIOjqwedHeCSBZSSaAAAAAAAA"
|
||||
//
|
||||
// val metadataFile = EncryptionUtils.deserializeJSON(
|
||||
// metadata,
|
||||
// object : TypeToken<EncryptedFolderMetadataFile>() {}
|
||||
// )
|
||||
// assertNotNull(metadataFile)
|
||||
//
|
||||
// val certJohnString = metadataFile.users[0].certificate
|
||||
// val certJohn = EncryptionUtils.convertCertFromString(certJohnString)
|
||||
//
|
||||
// val t1String = """-----BEGIN CERTIFICATE-----
|
||||
// MIIC6DCCAdCgAwIBAgIBADANBgkqhkiG9w0BAQUFADANMQswCQYDVQQDDAJ0MTAe
|
||||
// Fw0yMzA3MjUwNzU3MTJaFw00MzA3MjAwNzU3MTJaMA0xCzAJBgNVBAMMAnQxMIIB
|
||||
// IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtafHmDBcBqIu4HmMxMDW3j0S
|
||||
// y+S0YaKwHnBRt85KSwcEov0B5FOLuLknoBGx4Dn3u93ilThXXxacPMHeXL7WPuAs
|
||||
// 21/G7vsqwvrRRnCduf+FUO/AZeDCNErzpsQ8LmTa4PUloLPUcImpSjrHwhMs9Ekv
|
||||
// EbLRjbeSmSp9XvM+1fV/3jkT5jkOSnCFx5TGwGN5uHqwUir4UWXasvg253NK2XmW
|
||||
// ipKCDCR9TmH1baP3pNdoiChdmErT1c6E4DbBXpTw8XgP5ZbYH+qg1UQ/hC8nRJ3D
|
||||
// yCcHL+dg/GYraBMhDn4w2Vvq77xNNoNWQ9cT5Ay6cJbQLBQoJQirygQFrobYRQID
|
||||
// AQABo1MwUTAdBgNVHQ4EFgQUE9zCeA9/QMAtVgLxD23X6ZcodhMwHwYDVR0jBBgw
|
||||
// FoAUE9zCeA9/QMAtVgLxD23X6ZcodhMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
|
||||
// 9w0BAQUFAAOCAQEAZdy/YjJlvnz3FQwxp6oVtMJccpdxveEPfLzgaverhtd/vP8O
|
||||
// AvDzOLgQJHmrDS91SG503eU4cYGyuNKwd77OyTnqMg+GUEmJhGfPpSVrEIdh65jv
|
||||
// q61T4oqBdehevVmBq54rGiwL0DGv1DlXQlwiJZP4qni2KnOEFcnvL3gVtRnQjXQ+
|
||||
// kHvlMshkK6w021EMV5NfjG2zg67wC65rLaej5f6Ssp2S7g2VtmE4aXq1bjAuEbqk
|
||||
// 4TiyZHLDdsJuqzyGyyOpMV7i9ucXDoaZt9cGS9hT2vRxTrSH63vKR8Xeig9+stLw
|
||||
// t9ONcUqCKP7hd8rajtxM4JIIRExwD8OkgARWGg==
|
||||
// -----END CERTIFICATE-----"""
|
||||
//
|
||||
// val t1cert = EncryptionUtils.convertCertFromString(t1String)
|
||||
// val t1PrivateKeyKey = EncryptionUtils.PEMtoPrivateKey(encryptionTestUtils.t1PrivateKey)
|
||||
//
|
||||
// // val signed = encryptionUtilsV2.getMessageSignature(
|
||||
// // t1cert,
|
||||
// // t1PrivateKeyKey,
|
||||
// // metadataFile
|
||||
// // )
|
||||
//
|
||||
// assertTrue(encryptionUtilsV2.verifySignedMessage(signature1, metadata1, listOf(certJohn, t1cert)))
|
||||
// }
|
||||
|
||||
@Throws(Throwable::class)
|
||||
@Test
|
||||
fun testSigning() {
|
||||
val metadata =
|
||||
"""{"metadata": {"authenticationTag": "zMozev5R09UopLrq7Je1lw==","ciphertext": "j0OBtUrEt4IveGiexjm
|
||||
|GK7eKEaWrY70ZkteA5KxHDaZT/t2wwGy9j2FPQGpqXnW6OO3iAYPNgwFikI1smnfNvqdxzVDvhavl/IXa9Kg2niWyqK3D9
|
||||
|zpz0YD6mDvl0XsOgTNVyGXNVREdWgzGEERCQoyHI1xowt/swe3KCXw+lf+XPF/t1PfHv0DiDVk70AeWGpPPPu6yggAIxB4
|
||||
|Az6PEZhaQWweTC0an48l2FHj2MtB2PiMHtW2v7RMuE8Al3PtE4gOA8CMFrB+Npy6rKcFCXOgTZm5bp7q+J1qkhBDbiBYtv
|
||||
|dsYujJ52Xa5SifTpEhGeWWLFnLLgPAQ8o6bXcWOyCoYfLfp4Jpft/Y7H8qzHbPewNSyD6maEv+xljjfU7hxibbszz5A4Jj
|
||||
|MdQy2BDGoTmJx7Mas+g6l6ZuHLVbdmgQOvD3waJBy6rOg0euux0Cn4bB4bIFEF2KvbhdGbY1Uiq9DYa7kEmSEnlcAYaHyr
|
||||
|oTkDg4ew7ER0vIBBMzKM3r+UdPVKKS66uyXtZc=","nonce": "W+lxQJeGq7XAJiGfcDohkg=="},"users": [{"cert
|
||||
|ificate": "-----BEGIN CERTIFICATE-----\nMIIDkDCCAnigAwIBAgIBADANBgkqhkiG9w0BAQUFADBhMQswCQYDVQ
|
||||
|QGEwJERTEb\nMBkGA1UECAwSQmFkZW4tV3VlcnR0ZW1iZXJnMRIwEAYDVQQHDAlTdHV0dGdhcnQx\nEjAQBgNVBAoMCU5l
|
||||
|eHRjbG91ZDENMAsGA1UEAwwEam9objAeFw0yMzA3MTQwNzM0\nNTZaFw00MzA3MDkwNzM0NTZaMGExCzAJBgNVBAYTAkRF
|
||||
|MRswGQYDVQQIDBJCYWRl\nbi1XdWVydHRlbWJlcmcxEjAQBgNVBAcMCVN0dXR0Z2FydDESMBAGA1UECgwJTmV4\ndGNsb3
|
||||
|VkMQ0wCwYDVQQDDARqb2huMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEA7j3Er5YahJT0LAnSRLhpqbRo+E
|
||||
|1AVnt98rvp3DmEfBHNzNB+DS9IBDkS\nSXM/YtfAci6Tcw8ujVBjrZX/WEmrf8ynQHxYmSaJSnP8uAT306/MceZpdpruEc
|
||||
|9/\nS10a7vp54Zbld4NYdmfS71oVFVKgM7c/Vthx+rgu48fuxzbWAvVYLFcx47hz0DJT\nnjz2Za/R68uXpxfz7J9uEXYi
|
||||
|qsAs/FobDsLZluT3RyywVRwKBed1EZxUeLIJiyxp\nUthhGfIb8b3Vf9jZoUVi3m5gmc4spJQHvYAkfZYHzd9ras8jBu1a
|
||||
|bQRxcu2CYnVo\n6Y0mTxhKhQS/n5gjv3ExiQF3wp/XYwIDAQABo1MwUTAdBgNVHQ4EFgQUmTeILVuB\ntv70fTGkXWGAue
|
||||
|Dp5kAwHwYDVR0jBBgwFoAUmTeILVuBtv70fTGkXWGAueDp5kAw\nDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAA
|
||||
|OCAQEAyVtq9XAvW7nxSW/8\nhp30z6xbzGiuviXhy/Jo91VEa8IRsWCCn3OmDFiVduTEowx76tf8clJP0gk7Pozi\n6dg/
|
||||
|7Fin+FqQGXfCk8bLAh9gXKAikQ2GK8yRN3slRFwYC2mm23HrLdKXZHUqJcpB\nMz2zsSrOGPj1YsYOl/U8FU6KA7Yj7U3q
|
||||
|7kDMYTAgzUPZAH+d1DISGWpZsMa0RYid\nvigCCLByiccmS/Co4Sb1esF58H+YtV5+nFBRwx881U2g2TgDKF1lPMK/y3d8
|
||||
|B8mh\nUtW+lFxRpvyNUDpsMjOErOrtNFEYbgoUJLtqwBMmyGR+nmmh6xna331QWcRAmw0P\nnDO4ew==\n-----END CER
|
||||
|TIFICATE-----\n","encryptedMetadataKey": "HVT49bYmwXbGs/dJ2avgU9unrKnPf03MYUI5ZysSR1Bz5pqz64gz
|
||||
|H2GBAuUJ+Q4VmHtEfcMaWW7VXgzfCQv5xLBrk+RSgcLOKnlIya8jaDlfttWxbe8jJK+/0+QVPOc6ycA/t5HNCPg09hzj+g
|
||||
|nb2L89UHxL5accZD0iEzb5cQbGrc/N6GthjgGrgFKtFf0HhDVplUr+DL9aTyKuKLBPjrjuZbv8M6ZfXO93mOMwSZH3c3rw
|
||||
|DUHb/KEaTR/Og4pWQmrqr1VxGLqeV/+GKWhzMYThrOZAUz+5gsbckU2M5V9i+ph0yBI5BjOZVhNuDwW8yP8WtyRJwQc+UB
|
||||
|Rei/RGBQ==","userId": "john"}],"version": "2"}
|
||||
""".trimMargin()
|
||||
|
||||
val base64Metadata = EncryptionUtils.encodeStringToBase64String(metadata)
|
||||
|
||||
val privateKey = EncryptionUtils.PEMtoPrivateKey(encryptionTestUtils.t1PrivateKey)
|
||||
val certificateT1 = EncryptionUtils.convertCertFromString(encryptionTestUtils.t1PublicKey)
|
||||
val certificateEnc2 = EncryptionUtils.convertCertFromString(enc2Cert)
|
||||
|
||||
val signed = encryptionUtilsV2.signMessage(
|
||||
certificateT1,
|
||||
privateKey,
|
||||
metadata
|
||||
)
|
||||
|
||||
val base64Ans = encryptionUtilsV2.extractSignedString(signed)
|
||||
|
||||
// verify
|
||||
val certs = listOf(
|
||||
certificateEnc2,
|
||||
certificateT1
|
||||
)
|
||||
assertTrue(encryptionUtilsV2.verifySignedMessage(signed, certs))
|
||||
assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, base64Metadata, certs))
|
||||
}
|
||||
|
||||
@Throws(Throwable::class)
|
||||
@Test
|
||||
fun sign() {
|
||||
val sut = "randomstring123"
|
||||
val json = "randomstring123"
|
||||
val jsonBase64 = EncryptionUtils.encodeStringToBase64String(json)
|
||||
|
||||
val privateKey = EncryptionUtils.PEMtoPrivateKey(encryptionTestUtils.t1PrivateKey)
|
||||
val certificate = EncryptionUtils.convertCertFromString(encryptionTestUtils.t1PublicKey)
|
||||
|
||||
val signed = encryptionUtilsV2.signMessage(
|
||||
certificate,
|
||||
privateKey,
|
||||
sut
|
||||
)
|
||||
|
||||
val base64Ans = encryptionUtilsV2.extractSignedString(signed)
|
||||
|
||||
// verify
|
||||
val certs = listOf(
|
||||
EncryptionUtils.convertCertFromString(enc2Cert),
|
||||
certificate
|
||||
)
|
||||
assertTrue(encryptionUtilsV2.verifySignedMessage(signed, certs))
|
||||
assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, jsonBase64, certs))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testUpdateFileNameForEncryptedFile() {
|
||||
val folder = testFolder()
|
||||
|
||||
val metadata = EncryptionTestUtils().generateFolderMetadataV2(
|
||||
client.userId,
|
||||
EncryptionTestIT.publicKey
|
||||
)
|
||||
|
||||
RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, folder)
|
||||
|
||||
assertEquals(folder.decryptedRemotePath.contains("null"), false)
|
||||
}
|
||||
|
||||
/**
|
||||
* DecryptedFolderMetadata -> EncryptedFolderMetadata -> JSON -> encrypt -> decrypt -> JSON ->
|
||||
* EncryptedFolderMetadata -> DecryptedFolderMetadata
|
||||
*/
|
||||
@Test
|
||||
@Throws(Exception::class, Throwable::class)
|
||||
fun encryptionMetadataV2() {
|
||||
val decryptedFolderMetadata1: DecryptedFolderMetadataFile =
|
||||
EncryptionTestUtils().generateFolderMetadataV2(client.userId, EncryptionTestIT.publicKey)
|
||||
val root = OCFile("/")
|
||||
storageManager.saveFile(root)
|
||||
|
||||
val folder = OCFile("/enc")
|
||||
folder.parentId = storageManager.getFileByDecryptedRemotePath("/")?.fileId ?: throw IllegalStateException()
|
||||
|
||||
storageManager.saveFile(folder)
|
||||
|
||||
decryptedFolderMetadata1.filedrop.clear()
|
||||
|
||||
// encrypt
|
||||
val encryptedFolderMetadata1 = encryptionUtilsV2.encryptFolderMetadataFile(
|
||||
decryptedFolderMetadata1,
|
||||
client.userId,
|
||||
folder,
|
||||
storageManager,
|
||||
client,
|
||||
EncryptionTestIT.publicKey,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
|
||||
val signature = encryptionUtilsV2.getMessageSignature(enc1Cert, enc1PrivateKey, encryptedFolderMetadata1)
|
||||
|
||||
// serialize
|
||||
val encryptedJson = EncryptionUtils.serializeJSON(encryptedFolderMetadata1, true)
|
||||
|
||||
// de-serialize
|
||||
val encryptedFolderMetadata2 = EncryptionUtils.deserializeJSON(
|
||||
encryptedJson,
|
||||
object : TypeToken<EncryptedFolderMetadataFile?>() {}
|
||||
)
|
||||
|
||||
// decrypt
|
||||
val decryptedFolderMetadata2 = encryptionUtilsV2.decryptFolderMetadataFile(
|
||||
encryptedFolderMetadata2!!,
|
||||
getUserId(user),
|
||||
EncryptionTestIT.privateKey,
|
||||
folder,
|
||||
fileDataStorageManager,
|
||||
client,
|
||||
decryptedFolderMetadata1.metadata.counter,
|
||||
signature,
|
||||
user,
|
||||
targetContext,
|
||||
arbitraryDataProvider
|
||||
)
|
||||
|
||||
// compare
|
||||
assertTrue(
|
||||
EncryptionTestIT.compareJsonStrings(
|
||||
EncryptionUtils.serializeJSON(decryptedFolderMetadata1),
|
||||
EncryptionUtils.serializeJSON(decryptedFolderMetadata2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Throws(Throwable::class)
|
||||
@Test
|
||||
fun decryptFiledropV2() {
|
||||
val sut = EncryptedFiledrop(
|
||||
"""QE5nJmA8QC3rBJxbpsZu6MvkomwHMKTYf/3dEz9Zq3ITHLK/wNAIqWTbDehBJ7SlTfXakkKR9o0sOkUDI7PD8qJyv5hW7LzifszYGe
|
||||
|xE0V1daFcCFApKrIEBABHVOq+ZHJd8IzNSz3hdA9bWd2eiaEGyQzgdTPELE6Ie84IwFANJHcaRB5B43aaDdbUXNJ4/oMboOReKTJ
|
||||
|/vT6ZGhve4DRPEsez0quyDZDNlin5hD6UaUzw=
|
||||
""".trimMargin(),
|
||||
"HC87OgVzbR2CXdWp7rKI5A==",
|
||||
"7PSq7INkM2WKfmEPpRpTPA==",
|
||||
listOf(
|
||||
EncryptedFiledropUser(
|
||||
"android3",
|
||||
"""cNzk8cNyoTJ49Cj/x2WPlsMAnUWlZsfnKJ3VIRiczASeUYUFhaJpD8HDWE0uhkXSD7i9nzpe6pR7zllE7UE/QniDd+BQiF
|
||||
|80E5fSO1KVfFkLZRT+2pX5oPnl4CVtMnxb4xG7J1nAUqMhfS8PtQIr0+S7NKDdrUc41aNOB/4kH0D9LSo/bSC38L7ewv
|
||||
|mISM6ZFi1bfI1505kZV0HqcW12nZwHwe3s6rYkoSPBOPX1oPkvMYTVLkYuU+7DNL4HW7D9dc9X4bsSGLdj4joRi9FURi
|
||||
|mMv6MOrWOnYlX2zmMKAF3nEjLlhngKG7pUi/qMIlft2AhRM4cJuuIQ29vvTGFFDQ==
|
||||
""".trimMargin()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val privateKey =
|
||||
"""MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDPNCnYcPgGQwCzL8sxLTiE0atn5tiW1nfPQc1aY/+aXvkpF4h2vT
|
||||
|S/hQg2SCNFUlw8aYKksk5IH5FFcPv9QFG/TQDQOnZhi7moVPnVwLkx+cDfWQQs1EOhI/ZPdSo7MdaRLttbZZs/GJfnr1ziYZTxLO
|
||||
|UUxT541cnSpqGTKmUXhGMoX+jQTmcn1NyBD537NdetOxSdMfvBIobjRQ70/9c1HFGQSrJa+DmPiis6iFkd1LH6WWRbreC6DsRSqK
|
||||
|ne3sD1ujx39k+VxtBe035c2L9PbTMMW3kBdZxlRkV1tUQhDAys0K+CyvNIFsOjqvQKTnXNfWO+kVnpOkpbTK4imuPbAgMBAAECgf
|
||||
|9T537U/6TuwJLSj4bfYev8qYaakfVIpyMkL33e4YBQnUzhlCPBVYgpHkDPwznk2XhjQQiVcRAycmUHBmy4aPkcOjuBmd87aTj03k
|
||||
|niDk+doFDNU8myuwWTw/1fHdElRnLyZxEKrb391HD4SVVQMuxnw8UoC4iNcPnYneY/GTiTtB3dVcRKdabX3Oak2TFiJyJBtTz4RN
|
||||
|sRYVXM3jyCbxj8uV+XNr+3OuQe5u7cV5gkXOXHqcNczOrxGzSXVGULuw8FiHIlhId7tot3dGdyVvWD9YIwwGA/9/3g8JixqpQHKZ
|
||||
|6YJAeqltydisGa3CIIEzBAh52GJC7yzMKSC2ZAtW0CgYEA6B/O+EgtZthiXOwivqZmKKGgWGLSOGjVsExSa1iiTTz3EFwcdD54mU
|
||||
|TKc6hw787NFlfN1m7B7EDQxIldRDI3One1q2dj87taco/qFqKsHuAuC3gmZIp2F4l2P8NpdHHFMzUzsfs+grY/wLHZiJdfOTdulA
|
||||
|s9go5mDloMC96n0/UCgYEA5IQo7c4ZxwhlssIn89XaOlKGoIct07wsBMu47HZYFqgG2/NUN8zRfSdSvot+6zinAb6Z3iGZ2FBL+C
|
||||
|MmoEMGwuXSQjWxeD//UU6V5AZqlgis5s9WakKWmkTkVV3bPSwW0DuNcqbMk7BxAXcQ6QGIiBtzeaPuL/3gzA9e9vm8xo8CgYEAqL
|
||||
|I9S6nA/UZzLg8bLS1nf03/Z1ziZMajzk2ZdJRk1/dfow8eSskAAnvBGo8nDNFhsUQ8vwOdgeKVFtCx7JcGFkLbz+cC+CaIFExNFw
|
||||
|hASOwp6oH2fQk3y+FGBA8ze8IXTCD1IftzMbHb4WIfsyo3tTB497S3jkOJHhMJQDMgC2UCgYEAzjUgRe98vWkrdFLWAKfSxFxiFg
|
||||
|vF49JjGnTHy8HDHbbEccizD6NoyvooJb/1aMd3lRBtAtDpZhSXaTQ3D9lMCaWfxZV0LyH5AGLcyaasmfT8KU+iGEM8abuPHCWUyC
|
||||
|+36nJC4tn3s7I9V2gdP1Xd4Yx7+KFgN7huGVYpiM61dasCgYAQs5mPHRBeU+BHtPRyaLHhYq+jjYeocwyOpfw5wkiH3jsyUWTK9+
|
||||
|GlAoV75SYvQVIQS0VH1C1/ajz9yV02frAaUXbGtZJbyeAcyy3DjCc7iF0swJ4slP3gGVJipVF4aQ0d9wMoJ7SBaaTR0ohXeUWmTT
|
||||
|X+VGf+cZQ2IefKVnz9mg==
|
||||
""".trimMargin()
|
||||
|
||||
val decryptedFile = EncryptionUtilsV2().decryptFiledrop(sut, privateKey, arbitraryDataProvider, user)
|
||||
assertEquals("test.txt", decryptedFile.filename)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import androidx.test.espresso.idling.CountingIdlingResource
|
||||
|
||||
object EspressoIdlingResource {
|
||||
|
||||
private const val RESOURCE = "GLOBAL"
|
||||
|
||||
@JvmField val countingIdlingResource = CountingIdlingResource(RESOURCE)
|
||||
|
||||
fun increment() {
|
||||
countingIdlingResource.increment()
|
||||
}
|
||||
|
||||
fun decrement() {
|
||||
if (!countingIdlingResource.isIdleNow) {
|
||||
countingIdlingResource.decrement()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class FileExportUtilsIT : AbstractIT() {
|
||||
@Test
|
||||
fun exportFile() {
|
||||
val file = createFile("export.txt", 10)
|
||||
|
||||
val sut = FileExportUtils()
|
||||
|
||||
val expectedFile = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||
File("/sdcard/Downloads/export.txt")
|
||||
} else {
|
||||
File("/storage/emulated/0/Download/export.txt")
|
||||
}
|
||||
|
||||
assertFalse(expectedFile.exists())
|
||||
|
||||
sut.exportFile("export.txt", "/text/plain", targetContext.contentResolver, null, file)
|
||||
|
||||
assertTrue(expectedFile.exists())
|
||||
assertEquals(file.length(), expectedFile.length())
|
||||
assertTrue(expectedFile.delete())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun exportOCFile() {
|
||||
val file = createFile("export.txt", 10)
|
||||
val ocFile = OCFile("/export.txt").apply {
|
||||
storagePath = file.absolutePath
|
||||
}
|
||||
|
||||
val sut = FileExportUtils()
|
||||
|
||||
val expectedFile = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||
File("/sdcard/Downloads/export.txt")
|
||||
} else {
|
||||
File("/storage/emulated/0/Download/export.txt")
|
||||
}
|
||||
|
||||
assertFalse(expectedFile.exists())
|
||||
|
||||
sut.exportFile("export.txt", "/text/plain", targetContext.contentResolver, ocFile, null)
|
||||
|
||||
assertTrue(expectedFile.exists())
|
||||
assertEquals(file.length(), expectedFile.length())
|
||||
assertTrue(expectedFile.delete())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.OCFile
|
||||
import com.owncloud.android.utils.FileStorageUtils.checkIfEnoughSpace
|
||||
import com.owncloud.android.utils.FileStorageUtils.pathToUserFriendlyDisplay
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class FileStorageUtilsIT : AbstractIT() {
|
||||
private fun openFile(name: String): File {
|
||||
val ctx: Context = ApplicationProvider.getApplicationContext()
|
||||
val externalFilesDir = ctx.getExternalFilesDir(null)
|
||||
return File(externalFilesDir, name)
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun testEnoughSpaceWithoutLocalFile() {
|
||||
val ocFile = OCFile("/test.txt")
|
||||
val file = openFile("test.txt")
|
||||
file.createNewFile()
|
||||
|
||||
ocFile.storagePath = file.absolutePath
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 0
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(50L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(100L, ocFile))
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun testEnoughSpaceWithLocalFile() {
|
||||
val ocFile = OCFile("/test.txt")
|
||||
val file = openFile("test.txt")
|
||||
file.writeText("123123")
|
||||
|
||||
ocFile.storagePath = file.absolutePath
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 0
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(50L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(100L, ocFile))
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun testEnoughSpaceWithoutLocalFolder() {
|
||||
val ocFile = OCFile("/test/")
|
||||
val file = openFile("test")
|
||||
File(file, "1.txt").writeText("123123")
|
||||
|
||||
ocFile.storagePath = file.absolutePath
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 0
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(50L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(100L, ocFile))
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun testEnoughSpaceWithLocalFolder() {
|
||||
val ocFile = OCFile("/test/")
|
||||
val folder = openFile("test")
|
||||
folder.mkdirs()
|
||||
val file = File(folder, "1.txt")
|
||||
file.createNewFile()
|
||||
file.writeText("123123")
|
||||
|
||||
ocFile.storagePath = folder.absolutePath
|
||||
ocFile.mimeType = "DIR"
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 0
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertFalse(checkIfEnoughSpace(50L, ocFile))
|
||||
|
||||
ocFile.fileLength = 44
|
||||
assertTrue(checkIfEnoughSpace(50L, ocFile))
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertTrue(checkIfEnoughSpace(100L, ocFile))
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("MagicNumber")
|
||||
fun testEnoughSpaceWithNoLocalFolder() {
|
||||
val ocFile = OCFile("/test/")
|
||||
|
||||
ocFile.mimeType = "DIR"
|
||||
|
||||
ocFile.fileLength = 100
|
||||
assertTrue(checkIfEnoughSpace(200L, ocFile))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPathToUserFriendlyDisplay() {
|
||||
assertEquals("/", pathToUserFriendlyDisplay("/"))
|
||||
assertEquals("/sdcard/", pathToUserFriendlyDisplay("/sdcard/"))
|
||||
assertEquals("/sdcard/test/1/", pathToUserFriendlyDisplay("/sdcard/test/1/"))
|
||||
assertEquals("Internal storage/Movies/", pathToUserFriendlyDisplay("/storage/emulated/0/Movies/"))
|
||||
assertEquals("Internal storage/", pathToUserFriendlyDisplay("/storage/emulated/0/"))
|
||||
}
|
||||
|
||||
private fun pathToUserFriendlyDisplay(path: String): String {
|
||||
return pathToUserFriendlyDisplay(path, targetContext, targetContext.resources)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
|
||||
class FileUtilTest : AbstractIT() {
|
||||
@Test
|
||||
fun assertNullInput() {
|
||||
Assert.assertEquals("", FileUtil.getFilenameFromPathString(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertEmptyInput() {
|
||||
Assert.assertEquals("", FileUtil.getFilenameFromPathString(""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertFileInput() {
|
||||
val file = getDummyFile("empty.txt")
|
||||
Assert.assertEquals("empty.txt", FileUtil.getFilenameFromPathString(file.absolutePath))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertSlashInput() {
|
||||
val tempPath = File(FileStorageUtils.getTemporalPath(account.name) + File.pathSeparator + "folder")
|
||||
if (!tempPath.exists()) {
|
||||
Assert.assertTrue(tempPath.mkdirs())
|
||||
}
|
||||
Assert.assertEquals("", FileUtil.getFilenameFromPathString(tempPath.absolutePath))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertDotFileInput() {
|
||||
val file = getDummyFile(".dotfile.ext")
|
||||
Assert.assertEquals(".dotfile.ext", FileUtil.getFilenameFromPathString(file.absolutePath))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertFolderInput() {
|
||||
val tempPath = File(FileStorageUtils.getTemporalPath(account.name))
|
||||
if (!tempPath.exists()) {
|
||||
Assert.assertTrue(tempPath.mkdirs())
|
||||
}
|
||||
|
||||
Assert.assertEquals("", FileUtil.getFilenameFromPathString(tempPath.absolutePath))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertNoFileExtensionInput() {
|
||||
val file = getDummyFile("file")
|
||||
Assert.assertEquals("file", FileUtil.getFilenameFromPathString(file.absolutePath))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import com.nextcloud.client.account.UserAccountManager
|
||||
import com.nextcloud.client.account.UserAccountManagerImpl
|
||||
import com.nextcloud.client.mixins.SessionMixin
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.ui.activity.FileDisplayActivity
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class SessionMixinTest : AbstractIT() {
|
||||
|
||||
private lateinit var userAccountManager: UserAccountManager
|
||||
private lateinit var session: SessionMixin
|
||||
|
||||
private var scenario: ActivityScenario<FileDisplayActivity>? = null
|
||||
val intent = Intent(ApplicationProvider.getApplicationContext(), FileDisplayActivity::class.java)
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule<FileDisplayActivity>(intent)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
userAccountManager = UserAccountManagerImpl.fromContext(targetContext)
|
||||
|
||||
scenario = activityRule.scenario
|
||||
scenario?.onActivity { sut ->
|
||||
session = SessionMixin(
|
||||
sut,
|
||||
userAccountManager
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startAccountCreation() {
|
||||
session.startAccountCreation()
|
||||
|
||||
scenario = activityRule.scenario
|
||||
scenario?.onActivity { sut ->
|
||||
assert(sut.account.name == userAccountManager.accounts.first().name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2020 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils
|
||||
|
||||
import com.nextcloud.client.preferences.SubFolderRule
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.datamodel.MediaFolder
|
||||
import com.owncloud.android.datamodel.MediaFolderType
|
||||
import com.owncloud.android.datamodel.SyncedFolder
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Assert
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.util.Arrays
|
||||
|
||||
class SyncedFolderUtilsTest : AbstractIT() {
|
||||
@Test
|
||||
fun assertCoverFilenameUnqualified() {
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload(COVER))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("cover.JPG"))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("cover.jpeg"))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("cover.JPEG"))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("COVER.jpg"))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload(FOLDER))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("Folder.jpeg"))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("FOLDER.jpg"))
|
||||
Assert.assertFalse(SyncedFolderUtils.isFileNameQualifiedForAutoUpload(THUMBDATA_FILE))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertImageFilenameQualified() {
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("image.jpg"))
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("screenshot.JPG"))
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload(IMAGE_JPEG))
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("image.JPEG"))
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("SCREENSHOT.jpg"))
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload(SELFIE))
|
||||
Assert.assertTrue(SyncedFolderUtils.isFileNameQualifiedForAutoUpload("screenshot.PNG"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderNullSafe() {
|
||||
val folder: MediaFolder? = null
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderCustomQualified() {
|
||||
val folder = MediaFolder()
|
||||
folder.type = MediaFolderType.CUSTOM
|
||||
Assert.assertTrue(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderVideoUnqualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.VIDEO
|
||||
numberOfFiles = 0L
|
||||
}
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderVideoQualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.VIDEO
|
||||
numberOfFiles = 20L
|
||||
}
|
||||
Assert.assertTrue(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderImagesQualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.IMAGE
|
||||
numberOfFiles = 4L
|
||||
filePaths = Arrays.asList(
|
||||
getDummyFile(SELFIE).absolutePath,
|
||||
getDummyFile(SCREENSHOT).absolutePath,
|
||||
getDummyFile(IMAGE_JPEG).absolutePath,
|
||||
getDummyFile(IMAGE_BITMAP).absolutePath
|
||||
)
|
||||
}
|
||||
Assert.assertTrue(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderImagesEmptyUnqualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.IMAGE
|
||||
numberOfFiles = 0L
|
||||
}
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderImagesNoImagesUnqualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.IMAGE
|
||||
numberOfFiles = 3L
|
||||
filePaths = Arrays.asList(
|
||||
getDummyFile(SONG_ZERO).absolutePath,
|
||||
getDummyFile(SONG_ONE).absolutePath,
|
||||
getDummyFile(SONG_TWO).absolutePath
|
||||
)
|
||||
}
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderImagesMusicAlbumWithCoverArtUnqualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.IMAGE
|
||||
numberOfFiles = 3L
|
||||
filePaths = Arrays.asList(
|
||||
getDummyFile(COVER).absolutePath,
|
||||
getDummyFile(SONG_ONE).absolutePath,
|
||||
getDummyFile(SONG_TWO).absolutePath
|
||||
)
|
||||
}
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertMediaFolderImagesMusicAlbumWithFolderArtUnqualified() {
|
||||
val folder = MediaFolder().apply {
|
||||
absolutePath = getDummyFile(THUMBDATA_FOLDER).absolutePath
|
||||
type = MediaFolderType.IMAGE
|
||||
numberOfFiles = 3L
|
||||
filePaths = Arrays.asList(
|
||||
getDummyFile(FOLDER).absolutePath,
|
||||
getDummyFile(SONG_ONE).absolutePath,
|
||||
getDummyFile(SONG_TWO).absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertSyncedFolderNullSafe() {
|
||||
val folder: SyncedFolder? = null
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertUnqualifiedContentSyncedFolder() {
|
||||
val localFolder = getDummyFile(THUMBDATA_FOLDER + File.separatorChar)
|
||||
getDummyFile(THUMBDATA_FOLDER + File.separatorChar + THUMBDATA_FILE)
|
||||
val folder = SyncedFolder(
|
||||
localFolder.absolutePath,
|
||||
"",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
account.name,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
0L,
|
||||
MediaFolderType.IMAGE,
|
||||
false,
|
||||
SubFolderRule.YEAR_MONTH,
|
||||
false,
|
||||
SyncedFolder.NOT_SCANNED_YET
|
||||
)
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun assertUnqualifiedSyncedFolder() {
|
||||
getDummyFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_JPEG)
|
||||
getDummyFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_BITMAP)
|
||||
val folder = SyncedFolder(
|
||||
FileStorageUtils.getTemporalPath(account.name) + File.separatorChar + THUMBNAILS_FOLDER,
|
||||
"",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
account.name,
|
||||
1,
|
||||
1,
|
||||
true,
|
||||
0L,
|
||||
MediaFolderType.IMAGE,
|
||||
false,
|
||||
SubFolderRule.YEAR_MONTH,
|
||||
false,
|
||||
SyncedFolder.NOT_SCANNED_YET
|
||||
)
|
||||
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SELFIE = "selfie.png"
|
||||
private const val SCREENSHOT = "screenshot.JPG"
|
||||
private const val IMAGE_JPEG = "image.jpeg"
|
||||
private const val IMAGE_BITMAP = "image.bmp"
|
||||
private const val SONG_ZERO = "song0.mp3"
|
||||
private const val SONG_ONE = "song1.mp3"
|
||||
private const val SONG_TWO = "song2.mp3"
|
||||
private const val FOLDER = "folder.JPG"
|
||||
private const val COVER = "cover.jpg"
|
||||
private const val THUMBNAILS_FOLDER = ".thumbnails/"
|
||||
private const val THUMBDATA_FOLDER = "valid_folder/"
|
||||
private const val THUMBDATA_FILE = ".thumbdata4--1967290299"
|
||||
private const val ITERATION = 100
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setUp() {
|
||||
val tempPath =
|
||||
File(
|
||||
FileStorageUtils.getTemporalPath(account.name) + File.separatorChar +
|
||||
THUMBNAILS_FOLDER
|
||||
)
|
||||
if (!tempPath.exists()) {
|
||||
tempPath.mkdirs()
|
||||
}
|
||||
|
||||
createFile(SELFIE, ITERATION)
|
||||
createFile(SCREENSHOT, ITERATION)
|
||||
createFile(IMAGE_JPEG, ITERATION)
|
||||
createFile(IMAGE_BITMAP, ITERATION)
|
||||
createFile(SONG_ZERO, ITERATION)
|
||||
createFile(SONG_ONE, ITERATION)
|
||||
createFile(SONG_TWO, ITERATION)
|
||||
createFile(FOLDER, ITERATION)
|
||||
createFile(COVER, ITERATION)
|
||||
|
||||
createFile(THUMBDATA_FOLDER + File.separatorChar + THUMBDATA_FILE, ITERATION)
|
||||
createFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_JPEG, ITERATION)
|
||||
createFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_BITMAP, ITERATION)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun tearDown() {
|
||||
FileUtils.deleteDirectory(File(FileStorageUtils.getTemporalPath(account.name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Nextcloud - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
package com.owncloud.android.utils.theme
|
||||
|
||||
import com.owncloud.android.AbstractIT
|
||||
import com.owncloud.android.lib.resources.status.NextcloudVersion
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion
|
||||
import junit.framework.TestCase.assertFalse
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class CapabilityUtilsIT : AbstractIT() {
|
||||
@Test
|
||||
fun checkOutdatedWarning() {
|
||||
assertFalse(test(NextcloudVersion.nextcloud_28))
|
||||
assertFalse(test(NextcloudVersion.nextcloud_27))
|
||||
|
||||
assertTrue(test(NextcloudVersion.nextcloud_26))
|
||||
assertTrue(test(NextcloudVersion.nextcloud_25))
|
||||
assertTrue(test(NextcloudVersion.nextcloud_24))
|
||||
assertTrue(test(NextcloudVersion.nextcloud_23))
|
||||
assertTrue(test(NextcloudVersion.nextcloud_22))
|
||||
assertTrue(test(NextcloudVersion.nextcloud_21))
|
||||
assertTrue(test(OwnCloudVersion.nextcloud_20))
|
||||
assertTrue(test(OwnCloudVersion.nextcloud_19))
|
||||
assertTrue(test(OwnCloudVersion.nextcloud_18))
|
||||
assertTrue(test(OwnCloudVersion.nextcloud_17))
|
||||
assertTrue(test(OwnCloudVersion.nextcloud_16))
|
||||
}
|
||||
|
||||
private fun test(version: OwnCloudVersion): Boolean {
|
||||
return CapabilityUtils.checkOutdatedWarning(targetContext.resources, version, false)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue