From 653ec995ef43d6870b75302cf6eef4d8f97b3327 Mon Sep 17 00:00:00 2001 From: wangmengyan95 Date: Wed, 19 Aug 2015 00:01:03 -0700 Subject: [PATCH] Add test target --- ParseLoginUI/build.gradle | 5 + .../src/androidTest/AndroidManifest.xml | 30 + .../BaseActivityInstrumentationTestCase2.java | 62 ++ .../java/com/parse/ParseImageViewTest.java | 90 +++ .../java/com/parse/ParseQueryAdapterTest.java | 589 ++++++++++++++++++ .../java/com/parse/ui/TestActivity.java | 32 + .../src/androidTest/res/drawable/icon.png | Bin 0 -> 2574 bytes .../src/androidTest/res/layout/view_item.xml | 34 + ParseLoginUI/src/main/AndroidManifest.xml | 1 + 9 files changed, 843 insertions(+) create mode 100644 ParseLoginUI/src/androidTest/AndroidManifest.xml create mode 100644 ParseLoginUI/src/androidTest/java/com/parse/BaseActivityInstrumentationTestCase2.java create mode 100644 ParseLoginUI/src/androidTest/java/com/parse/ParseImageViewTest.java create mode 100644 ParseLoginUI/src/androidTest/java/com/parse/ParseQueryAdapterTest.java create mode 100644 ParseLoginUI/src/androidTest/java/com/parse/ui/TestActivity.java create mode 100644 ParseLoginUI/src/androidTest/res/drawable/icon.png create mode 100644 ParseLoginUI/src/androidTest/res/layout/view_item.xml diff --git a/ParseLoginUI/build.gradle b/ParseLoginUI/build.gradle index 3be03f0..29a99f9 100644 --- a/ParseLoginUI/build.gradle +++ b/ParseLoginUI/build.gradle @@ -8,6 +8,11 @@ dependencies { provided rootProject.ext.facebookSDK provided files("$rootProject.projectDir/ParseLoginUI/libs/ParseFacebookUtilsV4-1.10.0.jar") provided files("$rootProject.projectDir/ParseLoginUI/libs/ParseTwitterUtils-1.10.0.jar") + + androidTestCompile 'com.google.dexmaker:dexmaker:1.2' + androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' + androidTestCompile 'org.mockito:mockito-core:1.10.19' + androidTestCompile 'org.skyscreamer:jsonassert:1.2.3' } android { diff --git a/ParseLoginUI/src/androidTest/AndroidManifest.xml b/ParseLoginUI/src/androidTest/AndroidManifest.xml new file mode 100644 index 0000000..c06fd1f --- /dev/null +++ b/ParseLoginUI/src/androidTest/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/ParseLoginUI/src/androidTest/java/com/parse/BaseActivityInstrumentationTestCase2.java b/ParseLoginUI/src/androidTest/java/com/parse/BaseActivityInstrumentationTestCase2.java new file mode 100644 index 0000000..ac36416 --- /dev/null +++ b/ParseLoginUI/src/androidTest/java/com/parse/BaseActivityInstrumentationTestCase2.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package com.parse; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Context; +import android.test.ActivityInstrumentationTestCase2; + +public abstract class BaseActivityInstrumentationTestCase2 + extends ActivityInstrumentationTestCase2 { + + protected Activity activity = null; + + public BaseActivityInstrumentationTestCase2(Class activityClass) { + super(activityClass); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + activity = getActivity(); + + Instrumentation instrumentation = this.getInstrumentation(); + Context context = instrumentation.getTargetContext(); + // Wait for the application context to exist (to avoid an Android bug) + // http://stackoverflow.com/questions/6516441/why-does-androidtestcase-getcontext-getapplicationcontext-return-null + while (context == null || context.getApplicationContext() == null) { + Thread.sleep(100); + context = instrumentation.getTargetContext(); + } + + // Work around a bug with Mockito and dexmaker on 4.3: https://code.google.com/p/dexmaker/issues/detail?id=2 + System.setProperty("dexmaker.dexcache", context.getCacheDir().toString()); + } + + @Override + protected void tearDown() throws Exception { + activity = null; + super.tearDown(); + } +} diff --git a/ParseLoginUI/src/androidTest/java/com/parse/ParseImageViewTest.java b/ParseLoginUI/src/androidTest/java/com/parse/ParseImageViewTest.java new file mode 100644 index 0000000..0ede3ce --- /dev/null +++ b/ParseLoginUI/src/androidTest/java/com/parse/ParseImageViewTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package com.parse; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.test.InstrumentationTestCase; + +import com.parse.ui.test.R; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +public class ParseImageViewTest extends InstrumentationTestCase { + + public void testParseImageViewWithNullParseFile() throws Exception { + final Drawable drawable = new ColorDrawable(); + final ParseImageView imageView = new ParseImageView(getInstrumentation().getTargetContext()); + imageView.setPlaceholder(drawable); + imageView.setParseFile(null); + + byte[] data = ParseTaskUtils.wait(imageView.loadInBackground()); + + assertNull(data); + assertEquals(drawable, imageView.getDrawable()); + } + + public void testParseImageViewWithNotImageParseFile() throws Exception { + byte[] data = "hello".getBytes(); + ParseFile file = new ParseFile(data); + + final Drawable drawable = new ColorDrawable(); + final ParseImageView imageView = new ParseImageView(getInstrumentation().getTargetContext()); + imageView.setPlaceholder(drawable); + imageView.setParseFile(file); + + byte[] dataAgain = ParseTaskUtils.wait(imageView.loadInBackground()); + + assertTrue(Arrays.equals(data, dataAgain)); + // Since the parseFile can not be decode as an image, the getDrawable should not be changed + assertEquals(drawable, imageView.getDrawable()); + } + + public void testParseImageViewWithImageParseFile() throws Exception { + Context context = getInstrumentation().getTargetContext(); + final Drawable iconImage = context.getResources().getDrawable(R.drawable.icon); + Bitmap iconBitmap = ((BitmapDrawable) iconImage).getBitmap(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + iconBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + final byte[] imageData = stream.toByteArray(); + ParseFile file = new ParseFile(imageData); + + final Drawable drawable = new ColorDrawable(); + final ParseImageView imageView = new ParseImageView(context); + imageView.setPlaceholder(drawable); + imageView.setParseFile(file); + + byte[] dataAgain = ParseTaskUtils.wait(imageView.loadInBackground()); + + assertEquals(imageData, dataAgain); + assertNotNull(imageView.getDrawable()); + // It is hard to assert whether the two images are equal or not, so we just verify the image has + // been changed + assertNotSame(drawable, imageView.getDrawable()); + } +} + + diff --git a/ParseLoginUI/src/androidTest/java/com/parse/ParseQueryAdapterTest.java b/ParseLoginUI/src/androidTest/java/com/parse/ParseQueryAdapterTest.java new file mode 100644 index 0000000..cb7b9c9 --- /dev/null +++ b/ParseLoginUI/src/androidTest/java/com/parse/ParseQueryAdapterTest.java @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package com.parse; + +import android.database.DataSetObserver; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.parse.ParseQuery.CachePolicy; +import com.parse.ParseQueryAdapter.OnQueryLoadListener; +import com.parse.ParseQueryAdapter.QueryFactory; +import com.parse.ui.TestActivity; +import com.parse.ui.test.R; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import bolts.Capture; +import bolts.Task; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ParseQueryAdapterTest extends BaseActivityInstrumentationTestCase2 { + + @ParseClassName("Thing") + public static class Thing extends ParseObject { + public Thing() { + } + } + + public ParseQueryAdapterTest() { + super(TestActivity.class); + } + + private int TOTAL_THINGS = 10; + private List savedThings = new ArrayList(); + + @Override + public void setUp() throws Exception { + super.setUp(); + + // Register a mock cachedQueryController, the controller maintain a cache list and return + // results based on query state's CachePolicy + ParseQueryController queryController = mock(ParseQueryController.class); + Answer>> queryAnswer = new Answer>>() { + private List cachedThings = new ArrayList<>(); + + @Override + public Task> answer(InvocationOnMock invocation) throws Throwable { + ParseQuery.State state = (ParseQuery.State) invocation.getArguments()[0]; + int start = state.skip(); + // The default value of limit in ParseQuery is -1. + int end = state.limit() > 0 ? + Math.min(state.skip() + state.limit(), TOTAL_THINGS) : TOTAL_THINGS; + List things; + if (state.cachePolicy() == CachePolicy.CACHE_ONLY) { + try { + things = new ArrayList<>(cachedThings.subList(start, end)); + } catch (IndexOutOfBoundsException e) { + // Cache miss, throw exception + return Task.forError( + new ParseException(ParseException.CACHE_MISS, "results not cached")); + } + } else { + things = new ArrayList<>(savedThings.subList(start, end)); + // Update cache + for (int i = start; i < end; i++) { + if (i < cachedThings.size()) { + cachedThings.set(i, savedThings.get(i)); + } else { + cachedThings.add(i, savedThings.get(i)); + } + } + } + return Task.forResult(things); + } + }; + when(queryController.findAsync(any(ParseQuery.State.class), any(ParseUser.class), any(Task.class))) + .thenAnswer(queryAnswer); + ParseCorePlugins.getInstance().registerQueryController(queryController); + + // Register a mock currentUserController to make getSessionToken work + ParseCurrentUserController currentUserController = mock(ParseCurrentUserController.class); + when(currentUserController.getAsync()).thenReturn(Task.forResult(mock(ParseUser.class))); + when(currentUserController.getCurrentSessionTokenAsync()) + .thenReturn(Task.forResult(null)); + ParseCorePlugins.getInstance().registerCurrentUserController(currentUserController); + + ParseObject.registerSubclass(Thing.class); + // Make test data set + for (int i = 0; i < TOTAL_THINGS; i++) { + ParseObject thing = ParseObject.create("Thing"); + thing.put("aValue", i * 10); + thing.put("name", "Thing " + i); + thing.setObjectId(String.valueOf(i)); + savedThings.add(thing); + } + } + + @Override + public void tearDown() throws Exception { + savedThings = null; + ParseCorePlugins.getInstance().reset(); + ParseObject.unregisterSubclass("Thing"); + super.tearDown(); + } + + public void testLoadObjects() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + assertNull(e); + assertEquals(TOTAL_THINGS, objects.size()); + done.release(); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithGenericParseObjects() throws Exception { + final ParseQueryAdapter adapter = + new ParseQueryAdapter<>(activity, Thing.class); + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + assertNull(e); + assertEquals(TOTAL_THINGS, objects.size()); + done.release(); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testGetItemViewWithTextKey() { + ParseQueryAdapter adapter = + new ParseQueryAdapter<>(activity, Thing.class); + adapter.setTextKey("name"); + + View view = adapter.getItemView(savedThings.get(0), buildReusableListCell(), null); + TextView textView = (TextView) view.findViewById(android.R.id.text1); + + assertEquals("Thing 0", textView.getText()); + } + + public void testGetItemViewWithCustomLayout() { + ParseQueryAdapter adapter = + new ParseQueryAdapter<>(activity, Thing.class, R.layout.view_item); + adapter.setTextKey("name"); + + View view = adapter.getItemView(savedThings.get(0), null, null); + TextView textView = (TextView) view.findViewById(android.R.id.text1); + assertEquals("Thing 0", textView.getText()); + + // We should have inflated our own layout for the items. + assertNotNull(view.findViewById(android.R.id.message)); + } + + public void testGetItemViewWithNoTextKey() throws ParseException { + ParseQueryAdapter adapter = + new ParseQueryAdapter<>(activity, Thing.class); + + View view = adapter.getItemView(savedThings.get(0), null, null); + TextView textView = (TextView) view.findViewById(android.R.id.text1); + + // Since we do not set the textKey, we should display objectId + assertEquals(savedThings.get(0).getObjectId(), textView.getText()); + } + + public void testLoadObjectsWithLimitsObjectsPerPage() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + final int pageSize = 4; + adapter.setObjectsPerPage(pageSize); + final Capture timesThrough = new Capture<>(0); + final Semaphore done = new Semaphore(0); + final OnQueryLoadListener listener = new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + if (e != null) { + return; + } + + switch (timesThrough.get()) { + case 0: + // first time through, should have one page of results + "Load more" + assertEquals(pageSize, objects.size()); + assertEquals(pageSize + 1, adapter.getCount()); + adapter.loadNextPage(); + break; + case 1: + // second time through, should have two pages of results + "Load more" + assertEquals(pageSize, objects.size()); + assertEquals(2 * pageSize + 1, adapter.getCount()); + adapter.loadNextPage(); + break; + case 2: + // last time through, no "Load more" necessary. + assertEquals(TOTAL_THINGS - 2 * pageSize, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + done.release(); + } + timesThrough.set(timesThrough.get() + 1); + } + }; + adapter.addOnQueryLoadListener(listener); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithLimitsObjectsPerPageAndNoRemainder() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + final int pageSize = 5; + adapter.setObjectsPerPage(pageSize); + final Capture timesThrough = new Capture<>(0); + final Semaphore done = new Semaphore(0); + final OnQueryLoadListener listener = new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + if (e != null) { + return; + } + + switch (timesThrough.get()) { + case 0: + // first time through, should have one page of results + "Load more" cell + assertEquals(pageSize, objects.size()); + assertEquals(pageSize + 1, adapter.getCount()); + adapter.loadNextPage(); + break; + case 1: + // second time through, should have two pages' worth of results. It should realize that an + // additional "Load more" link isn't necessary, since this second page covers all of the + // results. + assertEquals(TOTAL_THINGS - pageSize, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + done.release(); + } + timesThrough.set(timesThrough.get() + 1); + } + }; + adapter.addOnQueryLoadListener(listener); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithPaginationNextPageView() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + final int pageSize = 5; + adapter.setObjectsPerPage(pageSize); + final Capture timesThrough = new Capture<>(0); + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + if (e != null) { + return; + } + + switch (timesThrough.get()) { + case 0: + assertEquals(pageSize, objects.size()); + assertEquals(pageSize + 1, adapter.getCount()); + + // Get Next Page view by passing in pageSize as the index + View view = adapter.getView(pageSize, null, null); + TextView textView = (TextView) view.findViewById(android.R.id.text1); + assertEquals("Load more...", textView.getText()); + // View should have OnClickListener attached. In API level 15+, we could call + // view.hasOnClickListeners() instead. + assertTrue(view.performClick()); + break; + case 1: + // Triggered by the performClick() call + done.release(); + } + timesThrough.set(timesThrough.get() + 1); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithNoPagination() throws Exception { + final int additional = 16; + for (int i = 0; i < additional; i++) { + ParseObject thing = ParseObject.create(Thing.class); + thing.put("name", "Additional Thing " + i); + savedThings.add(thing); + } + TOTAL_THINGS += additional; + + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + adapter.setPaginationEnabled(false); + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + assertNull(e); + assertEquals(TOTAL_THINGS, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + done.release(); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testClear() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + final Semaphore done = new Semaphore(0); + final Capture counter = new Capture<>(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + if (e != null) { + return; + } + switch (counter.get()) { + case 0: + assertEquals(TOTAL_THINGS, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + adapter.clear(); + assertEquals(0, adapter.getCount()); + adapter.loadObjects(); + break; + default: + assertEquals(TOTAL_THINGS, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + done.release(); + } + counter.set(counter.get() + 1); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithCacheThenNetworkQueryAndPagination() throws Exception { + QueryFactory factory = new QueryFactory() { + @Override + public ParseQuery create() { + ParseQuery query = new ParseQuery(Thing.class); + query.setCachePolicy(CachePolicy.CACHE_THEN_NETWORK); + return query; + } + }; + + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, factory); + final int pageSize = 5; + adapter.setObjectsPerPage(pageSize); + adapter.setPaginationEnabled(true); + final Capture timesThrough = new Capture<>(0); + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + } + + @Override + public void onLoaded(List objects, Exception e) { + if (e != null) { + return; + } + + switch (timesThrough.get()) { + case 0: + // Network callback for first page + assertEquals(pageSize, objects.size()); + assertEquals(pageSize + 1, adapter.getCount()); + adapter.loadNextPage(); + break; + case 1: + // Network callback for second page + assertEquals(TOTAL_THINGS - pageSize, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + adapter.loadObjects(); + break; + case 2: + // Cache callback for first page + assertEquals(pageSize, objects.size()); + assertEquals(pageSize + 1, adapter.getCount()); + break; + case 3: + // Network callback for first page + assertEquals(pageSize, objects.size()); + assertEquals(pageSize + 1, adapter.getCount()); + adapter.loadNextPage(); + break; + case 4: + // Cache callback for second page + assertEquals(TOTAL_THINGS - pageSize, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + break; + case 5: + // Network callback for second page + assertEquals(TOTAL_THINGS - pageSize, objects.size()); + assertEquals(TOTAL_THINGS, adapter.getCount()); + done.release(); + break; + } + timesThrough.set(timesThrough.get() + 1); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithOnLoadingAndOnLoadedCallback() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + adapter.setObjectsPerPage(5); + final Capture flag = new Capture<>(false); + final Semaphore done = new Semaphore(0); + + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + assertFalse(flag.get()); + flag.set(true); + assertEquals(0, adapter.getCount()); + } + + @Override + public void onLoaded(List objects, Exception e) { + assertTrue(flag.get()); + assertEquals(5, objects.size()); + done.release(); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithOverrideSetPageOnQuery() throws Exception { + final int arbitraryLimit = 3; + final ParseQueryAdapter adapter = + new ParseQueryAdapter(activity, Thing.class) { + @Override + public void setPageOnQuery(int page, ParseQuery query) { + // Make sure that this method is being used + respected. + query.setLimit(arbitraryLimit); + } + }; + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + }; + + @Override + public void onLoaded(List objects, Exception e) { + assertEquals(arbitraryLimit, objects.size()); + done.release(); + } + }); + + adapter.loadObjects(); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + public void testLoadObjectsWithtAutoload() throws Exception { + final ParseQueryAdapter adapter = new ParseQueryAdapter<>(activity, Thing.class); + DataSetObserver observer = new DataSetObserver() { }; + adapter.registerDataSetObserver(observer); + // Make sure that the Adapter doesn't start trying to load objects until AFTER we set this flag + // to true (= triggered by calling setAutoload, NOT registerDataSetObserver, if autoload is + // false). + final Capture flag = new Capture<>(false); + flag.set(true); + adapter.setAutoload(false); + final Semaphore done = new Semaphore(0); + adapter.addOnQueryLoadListener(new OnQueryLoadListener() { + @Override + public void onLoading() { + assertEquals(0, adapter.getCount()); + assertTrue(flag.get()); + } + + @Override + public void onLoaded(List objects, Exception e) { + assertEquals(TOTAL_THINGS, adapter.getCount()); + done.release(); + } + }); + + adapter.setAutoload(true); + + // Make sure we assert in callback is executed + assertTrue(done.tryAcquire(10, TimeUnit.SECONDS)); + } + + private LinearLayout buildReusableListCell() { + LinearLayout view = new LinearLayout(activity); + TextView textView = new TextView(activity); + textView.setId(android.R.id.text1); + view.addView(textView); + ParseImageView imageView = new ParseImageView(activity); + imageView.setId(android.R.id.icon); + view.addView(imageView); + return view; + } +} diff --git a/ParseLoginUI/src/androidTest/java/com/parse/ui/TestActivity.java b/ParseLoginUI/src/androidTest/java/com/parse/ui/TestActivity.java new file mode 100644 index 0000000..f1b50f8 --- /dev/null +++ b/ParseLoginUI/src/androidTest/java/com/parse/ui/TestActivity.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014, Parse, LLC. All rights reserved. + * + * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, + * copy, modify, and distribute this software in source code or binary form for use + * in connection with the web services and APIs provided by Parse. + * + * As with any software that integrates with the Parse platform, your use of + * this software is subject to the Parse Terms of Service + * [https://www.parse.com/about/terms]. This copyright notice shall be + * included in all copies or substantial portions of the software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package com.parse.ui; + +import android.app.Activity; +import android.os.Bundle; + +/** + * This is a blank Activity placeholder class for test, used to get the ParseQueryAdapterTest + * to satisfy the BaseActivityInstrumentationTestCase2 constructor. + */ +public class TestActivity extends Activity { +} diff --git a/ParseLoginUI/src/androidTest/res/drawable/icon.png b/ParseLoginUI/src/androidTest/res/drawable/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a07c69fa5a0f4da5d5efe96eea12a543154dbab6 GIT binary patch literal 2574 zcmV+p3i0)cP)Q`Og{P|8RRXpj5bgrSmEzSMfBn+{{vpNxw?;5UX;iv9sYxy_`IQHs$i<61a_iv^L>h8s-`D(`e@|IgS*Fj zNGM876Gf;3D8*1UX9a%v>yJKD*QkCwW2AirU(L{qNA)JghmGItc;(H<$!ABY&gBy1vJIEUj-b8%el*o|VkG)LqNx#TG>Jvj^jIte!!+RY z)T4j$7+PoF1AkRBf}R#^T=-q|PaK1$c<4UH)Hpq3$4WA|xtr!ZQLC=*vNE>O6E9kp+5X0eKB$6>C(lPwI@3#oY zhS_%x7e|j!$yG?ECXmh~EH~^OeuK}+sWoJse3Z3?ha3n`MM9KvA?uqpEnBg4Q46)7 zM$p%a$@l;+O}vfvx%XjH`}a{(-HHth9!JaUwV0*VqGR48^gWNYN<&~7x)y$e!X>e` zZ5!6KZoxbKuV9XUDI%#M1~IVh?pNSdeb~6@$y`v|yk=XK+fHxnDqnUK4&=QRNyIVf zYbDM*cI>~qIy*a7=z7uqkw@agd(<=y-Q7L!ty_23SGdXmahO<;N=wB+j;lNm%=OHC zy zU|>La6h%92y4IPufI$9>Xu!@y`TaNgtg&41@PwMwBdmSm7)xAWDLoqjZ==P2#*k7! z3o1)cVSI3KP_!?d8G^Lg0FtLXC~JYdxi|c%h~lXEixY=%VSFF@!*3&&9>(Rb|iK54Cx5;s~PY5iaV1het%w`dgQFBAJ;aFK zImQC}(|QaCFYUm1JVfzSc)ebv=)ObI)0jwJb``}Zj9J0n0Xgn*Zc(rFM9$xh_makZbm-at_v5^SW zM1y1SW@%+FuIy*WR)i3A2N_q;(YO`O!A|Ts^%z}9ZepCj3ytlw#x%N_fNrKKtPh`< z|1{UqF`4LxHaCQ79+E=uUXCOZ35jAMRz%R%0(P!0FMv=sk>Nr8%+OzY^c-M9@+fz=G`qa@v4sF5u-2289-#$**LWnyNNDwDf1( zkUiMnw|y$tn>pQP=Vn!#|17L^5AGrjtBkN$D@v)Z7LXc5EFhLB4<;7Wehh)CMqX|W zqsiZaO^benJ_hwa&V0ub$-_HUk**?g6fm9|!@kguU6*zhK)$qn-<3*kFrYPIaqR=V zUaUvk>@F_89b@tHs8R!*QKY;INJ<2_U+K6Ca3e9Gsl2{qY0%a7J?uICWgHuLfj+MB z=GkAN1&ifT#2u}B+2S#~$5jA(Qn^;H%CCmIae4AE-Dsng|Hl*Ov!z72k3ZnJs{pp| z+pW`DDueC#mEWOf=ucJ!dTL}hzOeiS-i?m2E;`EKz4<&Lu~NnW?peqVU^@<+T3KKu z{yrI%Qy-Z%HEvLUz}n^~m?7x`xuCtNR#L2En!T>dQtIKdS#V-Hzt3RtwTeYtmQ&dR z6qXZvac*oc@BUYEH%@Ylv_1&tSjkbzzU6*h1(3^C`;1z;g_SmOtclS?KWk2VYE zM*oS<=C483XckW?GN|1jfh3Ro(h + + + + + + diff --git a/ParseLoginUI/src/main/AndroidManifest.xml b/ParseLoginUI/src/main/AndroidManifest.xml index 9ceb853..9c1f128 100644 --- a/ParseLoginUI/src/main/AndroidManifest.xml +++ b/ParseLoginUI/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ ~ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ~ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> +