@Override public void onActivityStopped(Activity activity) { // Remove from the current list of running activities status.remove(activity.toString()); // If there are no running activities, the app is backgrounded // In this scenario, log the event to Mixpanel if (status.isEmpty()) { // Send the session tracking information to Mixpanel mixpanelCallbacks.track("$app_open"); // Mark the current session as inactive so we properly start a new one mixpanelCallbacks.unregisterSuperProperty("Session"); // Force all queued Mixpanel data to be sent to Mixpanel mixpanelCallbacks.flush(); } Log.d("Current Activities", "onStop() " + status.toString()); }
public void testHTTPFailures() { final List<Object> flushResults = new ArrayList<Object>(); final BlockingQueue<String> performRequestCalls = new LinkedBlockingQueue<String>(); final ServerMessage mockPoster = new ServerMessage() { @Override public byte[] performRequest(String endpointUrl, List<NameValuePair> nameValuePairs) throws IOException { if (null == nameValuePairs) { assertEquals( "DECIDE ENDPOINT?version=1&lib=android&token=Test+Message+Queuing&distinct_id=new+person", endpointUrl); return TestUtils.bytes("{}"); } Object obj = flushResults.remove(0); try { assertEquals(nameValuePairs.get(0).getName(), "data"); final String jsonData = Base64Coder.decodeString(nameValuePairs.get(0).getValue()); JSONArray msg = new JSONArray(jsonData); JSONObject event = msg.getJSONObject(0); performRequestCalls.put(event.getString("event")); if (obj instanceof IOException) { throw (IOException) obj; } else if (obj instanceof MalformedURLException) { throw (MalformedURLException) obj; } } catch (JSONException e) { throw new RuntimeException("Malformed data passed to test mock", e); } catch (InterruptedException e) { throw new RuntimeException( "Could not write message to reporting queue for tests.", e); } return (byte[]) obj; } }; final MPConfig config = new MPConfig(new Bundle()) { public String getDecideEndpoint() { return "DECIDE ENDPOINT"; } public String getEventsEndpoint() { return "EVENTS ENDPOINT"; } public boolean getDisableFallback() { return false; } }; final List<String> cleanupCalls = new ArrayList<String>(); final MPDbAdapter mockAdapter = new MPDbAdapter(getContext()) { @Override public void cleanupEvents(String last_id, Table table) { cleanupCalls.add("called"); super.cleanupEvents(last_id, table); } }; final AnalyticsMessages listener = new AnalyticsMessages(getContext()) { @Override protected MPDbAdapter makeDbAdapter(Context context) { return mockAdapter; } @Override protected ServerMessage getPoster() { return mockPoster; } @Override protected MPConfig getConfig(Context context) { return config; } }; MixpanelAPI metrics = new TestUtils.CleanMixpanelAPI(getContext(), mMockPreferences, "Test Message Queuing") { @Override protected AnalyticsMessages getAnalyticsMessages() { return listener; } }; try { // Basic succeed on first, non-fallback url cleanupCalls.clear(); flushResults.add(TestUtils.bytes("1\n")); metrics.track("Should Succeed", null); metrics.flush(); Thread.sleep(500); assertEquals("Should Succeed", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(null, performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(1, cleanupCalls.size()); // Fallback test--first URL throws IOException cleanupCalls.clear(); flushResults.add(new IOException()); flushResults.add(TestUtils.bytes("1\n")); metrics.track("Should Succeed", null); metrics.flush(); Thread.sleep(500); assertEquals("Should Succeed", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals("Should Succeed", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(1, cleanupCalls.size()); // Two IOExceptions -- assume temporary network failure, no cleanup should happen until // second flush cleanupCalls.clear(); flushResults.add(new IOException()); flushResults.add(new IOException()); flushResults.add(TestUtils.bytes("1\n")); metrics.track("Should Succeed", null); metrics.flush(); Thread.sleep(500); assertEquals("Should Succeed", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals("Should Succeed", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(0, cleanupCalls.size()); metrics.flush(); Thread.sleep(500); assertEquals("Should Succeed", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(null, performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(1, cleanupCalls.size()); // MalformedURLException -- should dump the events since this will probably never succeed cleanupCalls.clear(); flushResults.add(new MalformedURLException()); metrics.track("Should Fail", null); metrics.flush(); Thread.sleep(500); assertEquals("Should Fail", performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(null, performRequestCalls.poll(2, TimeUnit.SECONDS)); assertEquals(1, cleanupCalls.size()); } catch (InterruptedException e) { throw new RuntimeException("Test was interrupted."); } }
public void testMessageQueuing() { final BlockingQueue<String> messages = new LinkedBlockingQueue<String>(); final SynchronizedReference<Boolean> okToDecide = new SynchronizedReference<Boolean>(); okToDecide.set(false); final MPDbAdapter mockAdapter = new MPDbAdapter(getContext()) { @Override public int addJSON(JSONObject message, MPDbAdapter.Table table) { try { messages.put("TABLE " + table.getName()); messages.put(message.toString()); } catch (InterruptedException e) { throw new RuntimeException(e); } return super.addJSON(message, table); } }; mockAdapter.cleanupEvents(Long.MAX_VALUE, MPDbAdapter.Table.EVENTS); mockAdapter.cleanupEvents(Long.MAX_VALUE, MPDbAdapter.Table.PEOPLE); final ServerMessage mockPoster = new ServerMessage() { @Override public byte[] performRequest(String endpointUrl, List<NameValuePair> nameValuePairs) { final boolean decideIsOk = okToDecide.get(); if (null == nameValuePairs) { if (decideIsOk) { assertEquals( "DECIDE_ENDPOINT?version=1&lib=android&token=Test+Message+Queuing&distinct_id=new+person", endpointUrl); } else { fail( "User is unidentified, we shouldn't be checking decide. (URL WAS " + endpointUrl + ")"); } return TestUtils.bytes("{}"); } assertEquals(nameValuePairs.get(0).getName(), "data"); final String decoded = Base64Coder.decodeString(nameValuePairs.get(0).getValue()); try { messages.put("SENT FLUSH " + endpointUrl); messages.put(decoded); } catch (InterruptedException e) { throw new RuntimeException(e); } return TestUtils.bytes("1\n"); } }; final MPConfig mockConfig = new MPConfig(new Bundle()) { @Override public int getFlushInterval() { return -1; } @Override public int getBulkUploadLimit() { return 40; } @Override public String getEventsEndpoint() { return "EVENTS_ENDPOINT"; } @Override public String getPeopleEndpoint() { return "PEOPLE_ENDPOINT"; } @Override public String getDecideEndpoint() { return "DECIDE_ENDPOINT"; } }; final AnalyticsMessages listener = new AnalyticsMessages(getContext()) { @Override protected MPDbAdapter makeDbAdapter(Context context) { return mockAdapter; } @Override protected MPConfig getConfig(Context context) { return mockConfig; } @Override protected ServerMessage getPoster() { return mockPoster; } }; MixpanelAPI metrics = new TestUtils.CleanMixpanelAPI(getContext(), mMockPreferences, "Test Message Queuing") { @Override protected AnalyticsMessages getAnalyticsMessages() { return listener; } }; // Test filling up the message queue for (int i = 0; i < mockConfig.getBulkUploadLimit() - 1; i++) { metrics.track("frequent event", null); } metrics.track("final event", null); String expectedJSONMessage = "<No message actually received>"; try { for (int i = 0; i < mockConfig.getBulkUploadLimit() - 1; i++) { String messageTable = messages.poll(1, TimeUnit.SECONDS); assertEquals("TABLE " + MPDbAdapter.Table.EVENTS.getName(), messageTable); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONObject message = new JSONObject(expectedJSONMessage); assertEquals("frequent event", message.getString("event")); } String messageTable = messages.poll(1, TimeUnit.SECONDS); assertEquals("TABLE " + MPDbAdapter.Table.EVENTS.getName(), messageTable); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONObject message = new JSONObject(expectedJSONMessage); assertEquals("final event", message.getString("event")); String messageFlush = messages.poll(1, TimeUnit.SECONDS); assertEquals("SENT FLUSH EVENTS_ENDPOINT", messageFlush); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONArray bigFlush = new JSONArray(expectedJSONMessage); assertEquals(mockConfig.getBulkUploadLimit(), bigFlush.length()); metrics.track("next wave", null); metrics.flush(); String nextWaveTable = messages.poll(1, TimeUnit.SECONDS); assertEquals("TABLE " + MPDbAdapter.Table.EVENTS.getName(), nextWaveTable); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONObject nextWaveMessage = new JSONObject(expectedJSONMessage); assertEquals("next wave", nextWaveMessage.getString("event")); String manualFlush = messages.poll(1, TimeUnit.SECONDS); assertEquals("SENT FLUSH EVENTS_ENDPOINT", manualFlush); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONArray nextWave = new JSONArray(expectedJSONMessage); assertEquals(1, nextWave.length()); JSONObject nextWaveEvent = nextWave.getJSONObject(0); assertEquals("next wave", nextWaveEvent.getString("event")); okToDecide.set(true); metrics.getPeople().identify("new person"); metrics.getPeople().set("prop", "yup"); metrics.flush(); String peopleTable = messages.poll(1, TimeUnit.SECONDS); assertEquals("TABLE " + MPDbAdapter.Table.PEOPLE.getName(), peopleTable); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONObject peopleMessage = new JSONObject(expectedJSONMessage); assertEquals("new person", peopleMessage.getString("$distinct_id")); assertEquals("yup", peopleMessage.getJSONObject("$set").getString("prop")); String peopleFlush = messages.poll(1, TimeUnit.SECONDS); assertEquals("SENT FLUSH PEOPLE_ENDPOINT", peopleFlush); expectedJSONMessage = messages.poll(1, TimeUnit.SECONDS); JSONArray peopleSent = new JSONArray(expectedJSONMessage); assertEquals(1, peopleSent.length()); } catch (InterruptedException e) { fail("Expected a log message about mixpanel communication but did not recieve it."); } catch (JSONException e) { fail( "Expected a JSON object message and got something silly instead: " + expectedJSONMessage); } }