Skip to content

Add new ParseFile constructors and update save method #115

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions Parse/src/main/java/com/parse/ParseFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,40 @@ public String url() {
* successfully synced with the server.
*/
/* package for tests */ byte[] data;
/* package for tests */ File file;

/* package for tests */ final TaskQueue taskQueue = new TaskQueue();
private Set<Task<?>.TaskCompletionSource> currentTasks = Collections.synchronizedSet(
new HashSet<Task<?>.TaskCompletionSource>());

/**
* Creates a new file from a file pointer.
*
* @param file
* The file.
*/
public ParseFile(File file) {
this(file, null);
}

/**
* Creates a new file from a file pointer, and content type. Content type will be used instead of
* auto-detection by file extension.
*
* @param file
* The file.
* @param contentType
* The file's content type.
*/
public ParseFile(File file, String contentType) {
this(new State.Builder().name(file.getName()).mimeType(contentType).build());
if (file.length() > MAX_FILE_SIZE) {
throw new IllegalArgumentException(String.format("ParseFile must be less than %d bytes",
MAX_FILE_SIZE));
}
this.file = file;
}

/**
* Creates a new file from a byte array, file name, and content type. Content type will be used
* instead of auto-detection by file extension.
Expand Down Expand Up @@ -273,15 +302,30 @@ public Task<Void> then(Task<Void> task) throws Exception {
return Task.cancelled();
}

return getFileController().saveAsync(
state,
data,
sessionToken,
progressCallbackOnMainThread(uploadProgressCallback),
cancellationToken).onSuccessTask(new Continuation<State, Task<Void>>() {
Task<ParseFile.State> saveTask;
if (data != null) {
saveTask = getFileController().saveAsync(
state,
data,
sessionToken,
progressCallbackOnMainThread(uploadProgressCallback),
cancellationToken);
} else {
saveTask = getFileController().saveAsync(
state,
file,
sessionToken,
progressCallbackOnMainThread(uploadProgressCallback),
cancellationToken);
}

return saveTask.onSuccessTask(new Continuation<State, Task<Void>>() {
@Override
public Task<Void> then(Task<State> task) throws Exception {
state = task.getResult();
// Since we have successfully uploaded the file, we do not need to hold the file pointer
// anymore.
file = null;
return task.makeVoid();
}
});
Expand Down
145 changes: 121 additions & 24 deletions Parse/src/test/java/com/parse/ParseFileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;

import java.io.File;
Expand All @@ -21,12 +22,15 @@

import bolts.Task;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand All @@ -50,30 +54,41 @@ public void testConstructor() throws Exception {
String name = "name";
byte[] data = "hello".getBytes();
String contentType = "content_type";

ParseFile file = new ParseFile(name, data, contentType);
assertEquals("name", file.getName());
assertEquals("hello", new String(file.getData()));
assertEquals("content_type", file.getState().mimeType());
assertTrue(file.isDirty());

file = new ParseFile(data);
assertEquals("file", file.getName()); // Default
assertEquals("hello", new String(file.getData()));
assertEquals(null, file.getState().mimeType());
assertTrue(file.isDirty());

file = new ParseFile(name, data);
assertEquals("name", file.getName());
assertEquals("hello", new String(file.getData()));
assertEquals(null, file.getState().mimeType());
assertTrue(file.isDirty());

file = new ParseFile(data, contentType);
assertEquals("file", file.getName()); // Default
assertEquals("hello", new String(file.getData()));
assertEquals("content_type", file.getState().mimeType());
assertTrue(file.isDirty());
File file = temporaryFolder.newFile(name);

ParseFile parseFile = new ParseFile(name, data, contentType);
assertEquals("name", parseFile.getName());
assertEquals("hello", new String(parseFile.getData()));
assertEquals("content_type", parseFile.getState().mimeType());
assertTrue(parseFile.isDirty());

parseFile = new ParseFile(data);
assertEquals("file", parseFile.getName()); // Default
assertEquals("hello", new String(parseFile.getData()));
assertEquals(null, parseFile.getState().mimeType());
assertTrue(parseFile.isDirty());

parseFile = new ParseFile(name, data);
assertEquals("name", parseFile.getName());
assertEquals("hello", new String(parseFile.getData()));
assertEquals(null, parseFile.getState().mimeType());
assertTrue(parseFile.isDirty());

parseFile = new ParseFile(data, contentType);
assertEquals("file", parseFile.getName()); // Default
assertEquals("hello", new String(parseFile.getData()));
assertEquals("content_type", parseFile.getState().mimeType());
assertTrue(parseFile.isDirty());

// TODO(mengyan): Test file pointer in ParseFile when we have proper stage strategy
parseFile = new ParseFile(file);
assertEquals(name, parseFile.getName()); // Default
assertEquals(null, parseFile.getState().mimeType());
assertTrue(parseFile.isDirty());

parseFile = new ParseFile(file, contentType);
assertEquals(name, parseFile.getName()); // Default
assertEquals("content_type", parseFile.getState().mimeType());
}

@Test(expected = IllegalArgumentException.class)
Expand Down Expand Up @@ -157,6 +172,82 @@ public void testSaveAsyncCancelled() throws Exception {
Matchers.<Task<Void>>any());
}

@Test
public void testSaveAsyncSuccessWithData() throws Exception {
String name = "name";
byte[] data = "hello".getBytes();
String contentType = "content_type";
String url = "url";
ParseFile.State state = new ParseFile.State.Builder()
.url(url)
.build();
ParseFileController controller = mock(ParseFileController.class);
when(controller.saveAsync(
any(ParseFile.State.class),
any(byte[].class),
any(String.class),
any(ProgressCallback.class),
Matchers.<Task<Void>>any())).thenReturn(Task.forResult(state));
ParseCorePlugins.getInstance().registerFileController(controller);

ParseFile parseFile = new ParseFile(name, data, contentType);
ParseTaskUtils.wait(parseFile.saveAsync(null, null, null));

// Verify controller get the correct data
ArgumentCaptor<ParseFile.State> stateCaptor = ArgumentCaptor.forClass(ParseFile.State.class);
ArgumentCaptor<byte[]> dataCaptor = ArgumentCaptor.forClass(byte[].class);
verify(controller, times(1)).saveAsync(
stateCaptor.capture(),
dataCaptor.capture(),
any(String.class),
any(ProgressCallback.class),
Matchers.<Task<Void>>any());
assertNull(stateCaptor.getValue().url());
assertEquals(name, stateCaptor.getValue().name());
assertEquals(contentType, stateCaptor.getValue().mimeType());
assertArrayEquals(data, dataCaptor.getValue());
// Verify the state of ParseFile has been updated
assertEquals(url, parseFile.getUrl());
}

@Test
public void testSaveAsyncSuccessWithFile() throws Exception {
String name = "name";
File file = temporaryFolder.newFile(name);
String contentType = "content_type";
String url = "url";
ParseFile.State state = new ParseFile.State.Builder()
.url(url)
.build();
ParseFileController controller = mock(ParseFileController.class);
when(controller.saveAsync(
any(ParseFile.State.class),
any(File.class),
any(String.class),
any(ProgressCallback.class),
Matchers.<Task<Void>>any())).thenReturn(Task.forResult(state));
ParseCorePlugins.getInstance().registerFileController(controller);

ParseFile parseFile = new ParseFile(file, contentType);
ParseTaskUtils.wait(parseFile.saveAsync(null, null, null));

// Verify controller get the correct data
ArgumentCaptor<ParseFile.State> stateCaptor = ArgumentCaptor.forClass(ParseFile.State.class);
ArgumentCaptor<File> fileCaptor = ArgumentCaptor.forClass(File.class);
verify(controller, times(1)).saveAsync(
stateCaptor.capture(),
fileCaptor.capture(),
any(String.class),
any(ProgressCallback.class),
Matchers.<Task<Void>>any());
assertNull(stateCaptor.getValue().url());
assertEquals(name, stateCaptor.getValue().name());
assertEquals(contentType, stateCaptor.getValue().mimeType());
assertEquals(file, fileCaptor.getValue());
// Verify the state of ParseFile has been updated
assertEquals(url, parseFile.getUrl());
}

// TODO(grantland): testSaveAsyncNotDirtyAfterQueueAwait
// TODO(grantland): testSaveAsyncSuccess
// TODO(grantland): testSaveAsyncFailure
Expand All @@ -177,6 +268,12 @@ public void testTaskQueuedMethods() throws Exception {
any(String.class),
any(ProgressCallback.class),
Matchers.<Task<Void>>any())).thenReturn(Task.forResult(state));
when(controller.saveAsync(
any(ParseFile.State.class),
any(File.class),
any(String.class),
any(ProgressCallback.class),
Matchers.<Task<Void>>any())).thenReturn(Task.forResult(state));
when(controller.fetchAsync(
any(ParseFile.State.class),
any(String.class),
Expand Down