repo created

This commit is contained in:
Fr4nz D13trich 2025-09-18 17:54:51 +02:00
commit 1ef725ef20
2483 changed files with 278273 additions and 0 deletions

View 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);
}
}

View file

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

View 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());
}
}

View file

@ -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)
}
}
}

View 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());
}
}

View 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
}
}

View 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());
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}
}

View file

@ -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))
}
}

View file

@ -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)
)
}
}

View file

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

View file

@ -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)
}
}

View file

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

View file

@ -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)
}
}

View file

@ -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
}
}

View file

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

View file

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

View file

@ -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)
)
}
}
}
}

View 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)
}
}

View file

@ -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()

View file

@ -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)
}
}

View file

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

View file

@ -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)
}
}
}
}

View file

@ -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))
}
}

View file

@ -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")
}
}

View file

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

View 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()
}
}
}
}

View file

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

View file

@ -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)
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
}
}
}
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

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

View file

@ -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)
}
}

View file

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

View file

@ -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) }
}
}

View file

@ -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) }
}
}

View file

@ -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)
}
}

View file

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

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

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

View file

@ -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}")
}
}

View file

@ -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)
}
}

View file

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

View file

@ -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)
}
}

View file

@ -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
)
)
)
)
}
}

View file

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

View file

@ -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)
}
}

View file

@ -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
)
}
}

View file

@ -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)
}
}

View file

@ -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)
// }
}

View file

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

View file

@ -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)
}
}

View file

@ -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)
}
}
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

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

View file

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

View file

@ -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)
}
}

View file

@ -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))
}
}

View file

@ -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)
}
}
}

View file

@ -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)))
}
}
}

View file

@ -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)
}
}