Skip to content

Commit

Permalink
OONI Run v2 (#671)
Browse files Browse the repository at this point in the history
* Next generation of OONI Run - OONI Run v2!
* Updated UI of the cards displayed on the dashboard
* Test settings for individual tests on each card
* Changes to the "Run" button  to allow users to enable/disable specific tests, including both OONI Probe tests and installed OONI Run links

---------

Co-authored-by: Simone Basso <[email protected]>
  • Loading branch information
aanorbel and bassosimone authored Oct 21, 2024
1 parent a46360d commit e1207e7
Show file tree
Hide file tree
Showing 298 changed files with 14,059 additions and 3,870 deletions.
15 changes: 11 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ android {
applicationId 'org.openobservatory.ooniprobe'
minSdk libs.versions.minSdk.get().toInteger()
targetSdk libs.versions.targetSdk.get().toInteger()
versionName '3.9.0-beta'
versionCode 119
versionName '4.0.0'
versionCode 122
testInstrumentationRunner "org.openobservatory.ooniprobe.TestAndroidJUnitRunner"
buildConfigField 'String', 'OONI_API_BASE_URL', '"https://api.ooni.io/"'
buildConfigField 'String', 'OONI_API_BASE_URL', '"https://api.ooni.org"'
buildConfigField 'String', 'NOTIFICATION_SERVER', '"https://countly.ooni.io"'
buildConfigField 'String', 'OONI_RUN_DASHBOARD_URL', '"https://run.ooni.org"'
resValue "string", "APP_ID", 'org.openobservatory.ooniprobe'
resValue "string", "APP_NAME", "OONI Probe"
resValue "string", "RUN_V2_DOMAIN", "run.ooni.org"
buildConfigField 'String', 'SOFTWARE_NAME', 'BASE_SOFTWARE_NAME+IS_DEBUG'
buildConfigField 'String', 'COUNTLY_KEY', '"146836f41172f9e3287cab6f2cc347de3f5ddf3b"'
buildConfigField "boolean", "RUN_AUTOMATION", "false"
Expand Down Expand Up @@ -120,6 +122,10 @@ android {
viewBinding = true
buildConfig = true
}
dataBinding {
enabled = true
enabledForTests = true
}
namespace 'org.openobservatory.ooniprobe'
}

Expand All @@ -136,6 +142,7 @@ dependencies {
implementation libs.androidx.preference
implementation libs.androidx.localbroadcastmanager
implementation libs.androidx.legacy.support.v4
implementation libs.androidx.work.runtime

// Google
implementation libs.google.material
Expand Down Expand Up @@ -177,7 +184,7 @@ dependencies {
// Unit Testing
testImplementation project(':shared-test')
testImplementation libs.junit4
testImplementation libs.androidx.core
testImplementation libs.androidx.test.core
testImplementation libs.androidx.runner
testImplementation libs.androidx.rules
testImplementation libs.mockito.core
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,44 @@
package org.openobservatory.ooniprobe.ui;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.anyIntent;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.openobservatory.ooniprobe.ui.utils.RecyclerViewMatcher.withRecyclerView;
import static org.openobservatory.ooniprobe.ui.utils.ViewMatchers.withIndex;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.view.View;

import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.intent.Intents;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.adevinta.android.barista.rule.flaky.AllowFlaky;
import com.adevinta.android.barista.rule.flaky.FlakyTestRule;

import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openobservatory.ooniprobe.AbstractTest;
import org.openobservatory.ooniprobe.R;
import org.openobservatory.ooniprobe.activity.MainActivity;
import org.openobservatory.ooniprobe.activity.RunningActivity;
import org.openobservatory.ooniprobe.fragment.ProgressFragment;

import io.bloco.faker.Faker;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.ComponentNameMatchers.hasClassName;
import static androidx.test.espresso.intent.matcher.IntentMatchers.anyIntent;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.not;
import static org.openobservatory.ooniprobe.ui.utils.RecyclerViewMatcher.withRecyclerView;
import static org.openobservatory.ooniprobe.ui.utils.ViewMatchers.withIndex;

@RunWith(AndroidJUnit4.class)
public class MainActivityWebsitesTest extends AbstractTest {

Expand All @@ -50,6 +47,26 @@ public class MainActivityWebsitesTest extends AbstractTest {
@Rule
public FlakyTestRule flakyRule = new FlakyTestRule();

public static ViewAction clickChildViewWithId(final int id) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return null;
}

@Override
public String getDescription() {
return "Click on a child view with specified id.";
}

@Override
public void perform(UiController uiController, View view) {
View v = view.findViewById(id);
v.performClick();
}
};
}

@Test
@AllowFlaky(attempts = 3)
public void addCustomWebsiteTest() {
Expand All @@ -62,22 +79,17 @@ public void addCustomWebsiteTest() {

// Act
launchDashboard();
onView(withId(R.id.recycler))
.perform(scrollToPosition(0));
onView(withId(R.id.recycler)).perform(scrollToPosition(1));

onView(withRecyclerView(R.id.recycler)
.atPositionOnView(0, R.id.title))
.perform(click());
onView(withRecyclerView(R.id.recycler).atPositionOnView(1, R.id.title)).perform(click());

onView(withId(R.id.customUrl)).perform(click());

onView(withIndex(withId(R.id.editText), 0))
.perform(typeText(url1));
onView(withIndex(withId(R.id.editText), 0)).perform(typeText(url1));

onView(withId(R.id.add)).perform(click());

onView(withIndex(withId(R.id.editText), 1))
.perform(typeText(url2));
onView(withIndex(withId(R.id.editText), 1)).perform(typeText(url2));

// Assert
onView(withText(totalUrls)).check(matches(isDisplayed()));
Expand All @@ -90,32 +102,32 @@ public void deleteCustomWebsiteTest() {
Faker faker = new Faker();
String url1 = faker.internet.domainName() + faker.internet.domainSuffix();
String url2 = faker.internet.domainName() + faker.internet.domainSuffix();
String totalUrls = String.format(getResourceString(R.string.OONIRun_URLs), 1);
String totalUrls = String.format("Test %s URLs", 1);

// Act
launchDashboard();
onView(withId(R.id.recycler))
.perform(scrollToPosition(0));
onView(withId(R.id.recycler)).perform(scrollToPosition(1));

onView(withRecyclerView(R.id.recycler)
.atPositionOnView(0, R.id.title))
.perform(click());
onView(withRecyclerView(R.id.recycler).atPositionOnView(1, R.id.title)).perform(click());

onView(withId(R.id.customUrl)).perform(click());

onView(withIndex(withId(R.id.editText), 0))
.perform(typeText(url1));
onView(withIndex(withId(R.id.editText), 0)).perform(typeText(url1));

onView(withId(R.id.urlContainer)).perform(scrollToPosition(0));

onView(withIndex(withId(R.id.delete), 0))
.check(matches(not(isDisplayed())));
onView(withRecyclerView(R.id.urlContainer).atPositionOnView(0, R.id.delete)).check(matches(isDisplayed()));

onView(withId(R.id.add)).perform(click());

onView(withIndex(withId(R.id.editText), 1))
.perform(typeText(url2));
onView(withIndex(withId(R.id.editText), 1)).perform(typeText(url2));

onView(withIndex(withId(R.id.delete), 1))
.perform(click());
onView(withIndex(withId(R.id.delete), 1)).perform(click());

onView(withId(R.id.urlContainer)).perform(scrollToPosition(1));

// TODO: fix click action
onView(withRecyclerView(R.id.urlContainer).atPosition(1)).perform(RecyclerViewActions.actionOnItemAtPosition(1, clickChildViewWithId(R.id.delete)));

// Assert
onView(withText(totalUrls)).check(matches(isDisplayed()));
Expand All @@ -126,29 +138,18 @@ public void deleteCustomWebsiteTest() {
public void lunchCustomWebsiteIntentTest() {
// Act
launchDashboard();
onView(withId(R.id.recycler))
.perform(scrollToPosition(0));
onView(withId(R.id.recycler)).perform(scrollToPosition(1));

onView(withRecyclerView(R.id.recycler)
.atPositionOnView(0, R.id.title))
.perform(click());
onView(withRecyclerView(R.id.recycler).atPositionOnView(1, R.id.title)).perform(click());

onView(withId(R.id.customUrl)).perform(click());

Intents.init();

Intent emptyIntent = new Intent();
Instrumentation.ActivityResult result =
new Instrumentation.ActivityResult(Activity.RESULT_OK, emptyIntent);
Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(Activity.RESULT_OK, emptyIntent);
intending(anyIntent()).respondWith(result);


onView(withId(R.id.runButton)).perform(click());
// TODO(aanorbel): resolve issue. Running activity no longer available.
// ```intended(hasComponent(hasClassName(RunningActivity.class.getName())));```
// progress display is dismissed before its availability is checked
// ```onView(withId(R.id.progress_layout)).check(matches(isDisplayed()));```

Intents.release();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ServiceTestRule;

import com.adevinta.android.barista.rule.flaky.AllowFlaky;
import com.adevinta.android.barista.rule.flaky.FlakyTestRule;

import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -17,12 +14,12 @@
import org.openobservatory.ooniprobe.AbstractTest;
import org.openobservatory.ooniprobe.activity.MainActivity;
import org.openobservatory.ooniprobe.activity.RunningActivity;
import org.openobservatory.ooniprobe.common.OONITests;
import org.openobservatory.ooniprobe.common.service.RunTestService;
import org.openobservatory.ooniprobe.engine.TestEngineInterface;
import org.openobservatory.ooniprobe.model.jsonresult.EventResult;
import org.openobservatory.ooniprobe.test.EngineProvider;
import org.openobservatory.ooniprobe.test.suite.AbstractSuite;
import org.openobservatory.ooniprobe.test.suite.InstantMessagingSuite;
import org.openobservatory.ooniprobe.utils.DatabaseUtils;

import java.util.ArrayList;
Expand All @@ -31,6 +28,9 @@
import static org.openobservatory.ooniprobe.testing.ActivityAssertions.assertCurrentActivity;
import static org.openobservatory.ooniprobe.testing.ActivityAssertions.waitForCurrentActivity;

import com.adevinta.android.barista.rule.flaky.AllowFlaky;
import com.adevinta.android.barista.rule.flaky.FlakyTestRule;

@RunWith(AndroidJUnit4.class)
public class RunningActivityTest extends AbstractTest {

Expand Down Expand Up @@ -87,7 +87,7 @@ private void startRunTestService() {
serviceRule.startService(
new Intent(c, RunTestService.class)
.putExtra("testSuites", new ArrayList<AbstractSuite>() {{
add(new InstantMessagingSuite());
add(OONITests.WEBSITES.toOONIDescriptor(c).getTest(c));
}})
);
} catch (TimeoutException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
package org.openobservatory.ooniprobe.ui.resultdetails;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.adevinta.android.barista.rule.flaky.AllowFlaky;
import com.adevinta.android.barista.rule.flaky.FlakyTestRule;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openobservatory.ooniprobe.R;
import org.openobservatory.ooniprobe.common.OONITests;
import org.openobservatory.ooniprobe.factory.ResultFactory;
import org.openobservatory.ooniprobe.model.database.Measurement;
import org.openobservatory.ooniprobe.model.database.Result;
import org.openobservatory.ooniprobe.test.suite.CircumventionSuite;
import org.openobservatory.ooniprobe.utils.FormattingUtils;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.espresso.matcher.ViewMatchers.*;

import com.adevinta.android.barista.rule.flaky.AllowFlaky;
import com.adevinta.android.barista.rule.flaky.FlakyTestRule;

@RunWith(AndroidJUnit4.class)
public class CircumventionTest extends MeasurementAbstractTest {
Expand All @@ -32,7 +29,7 @@ public class CircumventionTest extends MeasurementAbstractTest {
@AllowFlaky(attempts = 3)
public void testHeaderData() {
// Arrange
Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 3, 0);
Result testResult = ResultFactory.createAndSave(OONITests.CIRCUMVENTION.toOONIDescriptor(c), 3, 0);

// Act
launchDetails(testResult.id);
Expand All @@ -44,7 +41,7 @@ public void testHeaderData() {
@Test
public void testSuccessPsiphon() {
// Arrange
Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 3, 0);
Result testResult = ResultFactory.createAndSave(OONITests.CIRCUMVENTION.toOONIDescriptor(c), 3, 0);
Measurement measurement = testResult.getMeasurement("psiphon");
String formattedBootstrap = FormattingUtils.formatBootstrap(measurement.getTestKeys().bootstrap_time);

Expand All @@ -61,7 +58,7 @@ public void testSuccessPsiphon() {
@Test
public void testBlockedPsiphon() {
// Arrange
Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 0, 3);
Result testResult = ResultFactory.createAndSave(OONITests.CIRCUMVENTION.toOONIDescriptor(c), 0, 3);
Measurement measurement = testResult.getMeasurement("psiphon");

// Act
Expand All @@ -77,7 +74,7 @@ public void testBlockedPsiphon() {
@Test
public void testSuccessTor() {
// Arrange
Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 3, 0);
Result testResult = ResultFactory.createAndSave(OONITests.CIRCUMVENTION.toOONIDescriptor(c), 3, 0);
Measurement measurement = testResult.getMeasurement("tor");

String formattedBridges = FormattingUtils.getFormattedBridges(measurement);
Expand All @@ -97,7 +94,7 @@ public void testSuccessTor() {
@Test
public void testBlockedTor() {
// Arrange
Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 0, 3);
Result testResult = ResultFactory.createAndSave(OONITests.CIRCUMVENTION.toOONIDescriptor(c), 0, 3);
Measurement measurement = testResult.getMeasurement("tor");

String formattedBridges = FormattingUtils.getFormattedBridges(measurement);
Expand Down
Loading

0 comments on commit e1207e7

Please sign in to comment.