Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test CreateFolderOperation without server #3624

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

tobiasKaminsky
Copy link
Member

We have CreateFolderRemoteOperation which is in library and is (or should be) tested there.
Goal of this PR is to test CreateFolderOperation without really using CreateFolderRemoteOperation or any other RemoteOperation, so that this is all Unit Testing.

It is meant to be a start, where we can discuss.

Some consideration:

  • I could also have mocked FileDataStorageManager and test the class in a separate unit test
  • I commented Log_OC.d(…) as I have no idea how to mock this: maybe same approach like with PreferencesManager? But we use this way more often…
  • Tests for failure, multiple folder structure, etc. are missing
  • I used a method "wrapper" for CreateFolderRemoteOperation and ReadFolderRemoteOperation, maybe there is something easier?

@ezaquarii maybe you have some input in this.

Signed-off-by: tobiasKaminsky [email protected]

@codecov
Copy link

codecov bot commented Feb 14, 2019

Codecov Report

Merging #3624 into master will decrease coverage by 12.66%.
The diff coverage is 66.66%.

@@             Coverage Diff              @@
##             master   #3624       +/-   ##
============================================
- Coverage     18.71%   6.04%   -12.67%     
+ Complexity        3       1        -2     
============================================
  Files           392     315       -77     
  Lines         34273   30780     -3493     
  Branches       5011    4419      -592     
============================================
- Hits           6414    1862     -4552     
- Misses        26863   28640     +1777     
+ Partials        996     278      -718
Impacted Files Coverage Δ Complexity Δ
...a/com/owncloud/android/utils/FileStorageUtils.java 11.03% <ø> (-15.76%) 0 <0> (ø)
...loud/android/datamodel/FileDataStorageManager.java 10.52% <ø> (-38.5%) 0 <0> (ø)
...cloud/android/operations/common/SyncOperation.java 25% <0%> (-12.5%) 0 <0> (ø)
...android/operations/SynchronizeFolderOperation.java 0% <0%> (ø) 0 <0> (ø) ⬇️
...loud/android/datamodel/ThumbnailsCacheManager.java 15.08% <100%> (-18.13%) 0 <0> (ø)
...in/java/com/owncloud/android/datamodel/OCFile.java 58.06% <100%> (-8.31%) 0 <0> (ø)
...loud/android/operations/CreateFolderOperation.java 74.41% <100%> (ø) 0 <0> (ø) ⬇️
...va/com/owncloud/android/utils/OwnCloudSession.java 0% <0%> (-100%) 0% <0%> (ø)
...ncloud/android/ui/adapter/FeaturesViewAdapter.java 0% <0%> (-100%) 0% <0%> (ø)
...com/owncloud/android/ui/events/TokenPushEvent.java 0% <0%> (-100%) 0% <0%> (ø)
... and 310 more

@AndyScherzinger
Copy link
Member

Looks good code-wise on an cellphone.
As for the logger, well I would expect a logger to be usable in a static way, so I wouldn't go for the same approach as for the preference manager. Maybe we could have a method injecting the writer within the logger class so that for testing it writes to something like a list or simply doesn't write or store anything anywhere.

@tobiasKaminsky
Copy link
Member Author

Maybe we could have a method injecting the writer within the logger class so that for testing it writes to something like a list or simply doesn't write or store anything anywhere.

Right, like with FileDataStorageManagerInterface, we could have Log_OCInterface…
But is this the best approach…

Copy link
Collaborator

@ezaquarii ezaquarii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Injecting Logger as a dependency is an elegant solution, but at a cost of convenience and there is apparently no appetite for this.

That is understandable - printf() is so nice to use.

Given the constraints, I'd extract logger implementation into a class (LogFileWriter, LogNullWriter, LogArrayWriter, etc, etc), and delegate all calls in Log_OC to specific runtime version.

You can swap runtime version with a static setter at any time, let's say in test harness setup.

Pseudocode. I skipped thread safety for the sake of illustration. Also, pls note that you can either inject a specific logger instance to anything and/or use a static, convenient Log_OC interface.

interface LoggerInterface {
   void d(...)
   etc, etc
}

class FileLogger implements LoggerInterface {
    // move existing file-based logger logic from Log_OC here
}

class NullLogger implements LoggerInterface {
   // do nothing :)
}

class MemoryLogger implements LoggerInterface {
   // write logs to ListArray
}

/**
 * BTW, Does it still need _OC suffix?
 */
public class Log_OC {
    private static LoggerInterface sLogger;

    /**
     * You can probably move this to a separate class, and use package-scope to remove
     * init() from public Log_OC API.
     */
    public static void init(LoggerInterface logger) {
         sLogger = logger;
    }

    /**
     * Another variant is package-scoped. Let's imagine that this API
     * is available via some LoggerControl or LoggerManager API that
     * is in the same pkg
     */
    static void init(LoggerInterface logger) {
         sLogger = logger;
    }

    public static void d(Object object, String message) {
         sLogger.d(object, message);
    }
    ...
}

/**
 * You can get fancy with that and make yourself
 * a nice settings panel to fiddle with logger at runtime.
 */
class LoggerManager {
    public static init(LoggerInterface ....);
    public static setLogLevel(...);
    public static setSomeOtherFancyLogOption(...)
}

@@ -76,6 +76,14 @@ protected RemoteOperationResult run(OwnCloudClient client) {
return result;
}

public CreateFolderRemoteOperation createCreateFolderRemoteOperation(String mRemotePath, boolean mCreateFullPath) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those m prefixes are probably a left-overs after either refactoring or automated generation? It can be trimmed with Shift-F6 in no time.


@Override
public boolean fileExists(long id) {
throw new IllegalArgumentException("to implement");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about UnsupportedOperationException?

@tobiasKaminsky
Copy link
Member Author

As this seems to be the right way, thanks @ezaquarii, I'll do three PRs:

  • create FileDataStorageManagerInterface
  • create Log Interface
  • create Test PR based on both above

So this clean and easy to review.

@AndyScherzinger
Copy link
Member

So @ezaquarii and @tobiasKaminsky I'd like to start the interface naming discussion :D
My vote would be to no prefix or suffix the interface names. Usually we probably always work with the interface so the code is easier to read and less verbose. As for implementing classes I would even suggest the same thing as in to choose a proper name for the class which hints to the class' intent and implementation case. In edge cases where we can't find a name for the class we then could add an Impl suffix but rather as some kind of a last resort.

@AndyScherzinger
Copy link
Member

Example

  • FileDataStorageManager (Inferface),
  • FileDataStorageManagerLocal (for Test) and
  • FileDataStorageManagerDB (for SQLite/App)

@ezaquarii
Copy link
Collaborator

ezaquarii commented Feb 15, 2019

Example

* FileDataStorageManager (Inferface),

* FileDataStorageManagerLocal (for Test) and

* FileDataStorageManagerDB (for SQLite/App)

It's not a bad practice to optimize for most frequent expected use case:

  1. there is an interface (obviously)
  2. there is only 1 default implementation
  3. there is at most 1 mock

It boils down to choosing between 2 industry wide conventions that happen to work:

  1. IFoo + Foo + FooTest + some Runnable-style exceptions
  2. Foo + FooImpl + FooImplTest/FooTest + (matches default Runnable style, but has Impl)

Each one of them has some corner aesthetic issues, philosophical foundations, fans and haters. I have my opinion of course, but it's not important as long as:

  1. you are not intrusive
  2. you are consistent across the stack.

I believe Android picked Foo-FooImpl variant.

And if some day you find yourself on the wrong side of the flame war: Shift+F6. ;)

@AndyScherzinger
Copy link
Member

AndyScherzinger commented Feb 15, 2019

Well, in that case I vote for variant 2 proposed by @ezaquarii since that should fit the most frequent case (just my opinion, no proof) and as stated would also be the typical Android naming schema pick.

@ezaquarii
Copy link
Collaborator

Is it going to be merged? Still WIP?

@tobiasKaminsky
Copy link
Member Author

Is it going to be merged? Still WIP?

Yes, I just need to find a bit more time… :-)

@nextcloud nextcloud deleted a comment Apr 17, 2019
@nextcloud nextcloud deleted a comment Apr 17, 2019
@nextcloud-android-bot
Copy link
Collaborator

@nextcloud nextcloud deleted a comment May 14, 2019
@nextcloud nextcloud deleted a comment May 14, 2019
@nextcloud-android-bot
Copy link
Collaborator

@nextcloud nextcloud deleted a comment May 30, 2019
@nextcloud nextcloud deleted a comment May 30, 2019
@nextcloud nextcloud deleted a comment Dec 12, 2019
@nextcloud-android-bot
Copy link
Collaborator

Codacy

329

Lint

TypemasterPR
Warnings7373
Errors00

SpotBugs (new)

Warning TypeNumber
Bad practice Warnings26
Correctness Warnings72
Internationalization Warnings13
Malicious code vulnerability Warnings5
Multithreaded correctness Warnings9
Performance Warnings107
Security Warnings44
Dodgy code Warnings137
Total413

SpotBugs (master)

Warning TypeNumber
Bad practice Warnings26
Correctness Warnings70
Internationalization Warnings13
Malicious code vulnerability Warnings5
Multithreaded correctness Warnings9
Performance Warnings106
Security Warnings44
Dodgy code Warnings136
Total409

SpotBugs increased!

Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
Signed-off-by: tobiasKaminsky <[email protected]>
@nextcloud-android-bot
Copy link
Collaborator

Codacy Here is an overview of what got changed by this pull request:

Issues
======
+ Solved 27
- Added 11
           

Complexity increasing per file
==============================
- src/main/java/com/owncloud/android/datamodel/FileDataStorageManagerImpl.java  21
         

See the complete overview on Codacy

for (Account account : accountManager.getAccounts()) {
final FileDataStorageManager storageManager = new FileDataStorageManager(account, contentResolver);
final FileDataStorageManager storageManager = new FileDataStorageManagerImpl(account, getContext());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public void copyLocalFile(OCFile file, String targetPath) {

if (file != null && file.fileExists() && !OCFile.ROOT_PATH.equals(file.getFileName())) {
String localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

);
} catch (RemoteException e) {
Log_OC.e(TAG, e.getMessage(), e);
return ret;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -92,8 +93,7 @@ protected Result onRunJob(@NonNull Params params) {
Account[] accounts = userAccountManager.getAccounts();

for (Account account : accounts) {
FileDataStorageManager storageManager = new FileDataStorageManager(account,
getContext().getContentResolver());
FileDataStorageManager storageManager = new FileDataStorageManagerImpl(account, getContext());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cv.put(
ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
file.getModificationTimestampAtLastSyncForData()
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContentValues cv = new ContentValues();
fileId[0] = String.valueOf(cursor.getLong(cursor.getColumnIndex(ProviderTableMeta._ID)));
String oldFileStoragePath =
cursor.getString(cursor.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result_uri = getContentProviderClient().insert(ProviderTableMeta.CONTENT_URI_SHARE, cv);
} catch (RemoteException e) {
Log_OC.e(TAG, FAILED_TO_INSERT_MSG + e.getMessage(), e);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Retrieves an stored {@link OCShare} given its id.
*
* @param id Identifier.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


}
if (child.getRemotePath().equals(file.getRemotePath())) {
cv.put(ProviderTableMeta.FILE_PARENT, targetParent.getFileId());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
cursor.close();

/// 3. apply updates in batch
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nextcloud-android-bot
Copy link
Collaborator

APK file: https://www.kaminsky.me/nc-dev/android-artifacts/12232.apk

qrcode

To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.

@nextcloud-android-bot
Copy link
Collaborator

Codacy

340

Lint

TypemasterPR
Warnings7676
Errors00

SpotBugs (new)

Warning TypeNumber
Bad practice Warnings26
Correctness Warnings73
Internationalization Warnings13
Malicious code vulnerability Warnings5
Multithreaded correctness Warnings9
Performance Warnings108
Security Warnings44
Dodgy code Warnings140
Total418

SpotBugs (master)

Warning TypeNumber
Bad practice Warnings26
Correctness Warnings71
Internationalization Warnings13
Malicious code vulnerability Warnings5
Multithreaded correctness Warnings9
Performance Warnings107
Security Warnings44
Dodgy code Warnings139
Total414

SpotBugs increased!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants