Skip to content
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
17 changes: 14 additions & 3 deletions .github/workflows/build-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ name: Build for Android
on:
push:
branches:
maharrim/*

- master
- release/*
- buildme/*
- maharrim/*
pull_request:
branches:
- master
jobs:
build:
runs-on: windows-latest
Expand All @@ -21,7 +26,13 @@ jobs:
run: |
cd "$Env:GITHUB_WORKSPACE\lib\android_build"
.\gradlew.bat assemble
dir maesdk\build\outputs
.\gradlew.bat maesdk:test
- name: Upload Reports
if: failure()
uses: actions/upload-artifact@v1
with:
name: reports
path: lib\android_build\maesdk\build\reports
- name: Upload Artifacts
uses: actions/upload-artifact@v1
with:
Expand Down
93 changes: 47 additions & 46 deletions lib/android_build/maesdk/build.gradle
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"


defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
externalNativeBuild {
cmake {
// Passes optional arguments to CMake.
arguments "-DANDROID_STL=c++_shared"
}
}
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.16.2"
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
apply plugin: 'com.android.library'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"


defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
externalNativeBuild {
cmake {
// Passes optional arguments to CMake.
arguments "-DANDROID_STL=c++_shared"
}
}
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.16.2"
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-inline:3.2.4'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ final public void onCapabilitiesChanged(Network network, NetworkCapabilities net

class Request implements Runnable {

Request(String url, String method, byte[] body, String request_id, int[] header_length, byte[] header_buffer)
Request(httpClient parent, String url, String method, byte[] body, String request_id, int[] header_length, byte[] header_buffer)
throws java.net.MalformedURLException, java.io.IOException {
m_url = new URL(url);
m_parent = parent;
m_url = parent.newUrl(url);
m_connection = (HttpURLConnection) m_url.openConnection();
m_connection.setRequestMethod(method);
m_body = body;
Expand Down Expand Up @@ -150,21 +151,14 @@ public void run() {
} finally {
m_connection.disconnect();
}
dispatchCallback(m_request_id, response, headerArray, body);
}

public void dispatchCallback(String id, int response, Object[] headers, byte[] body) {
// this stub makes it easier to mock this method in
// Java unit tests.
nativeDispatchCallback(id, response, headers, body);
m_parent.dispatchCallback(m_request_id, response, headerArray, body);
}

public native void nativeDispatchCallback(String id, int response, Object[] headers, byte[] body);

private URL m_url;
private HttpURLConnection m_connection;
private byte[] m_body = {};
public String m_request_id;
protected httpClient m_parent;
}

public class httpClient {
Expand All @@ -174,10 +168,10 @@ public httpClient(android.content.Context context) {
m_context = context;
String path = System.getProperty("java.io.tmpdir");
setCacheFilePath(path);
m_executor = Executors.newFixedThreadPool(MAX_HTTP_THREADS);
m_executor = createExecutor();
createClientInstance();
// We need API 24 to follow changes in network status
if (Build.VERSION.SDK_INT >= 24) {
if (hasConnectivityManager()) {
m_connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
boolean is_metered = m_connectivityManager.isActiveNetworkMetered();
m_callback = new ConnectivityCallback(this, is_metered);
Expand All @@ -190,6 +184,16 @@ public httpClient(android.content.Context context) {
m_power_receiver.onReceive(context, status);
}

protected ExecutorService createExecutor()
{
return Executors.newFixedThreadPool(MAX_HTTP_THREADS);
}

protected boolean hasConnectivityManager()
{
return Build.VERSION.SDK_INT >= 24;
}

public void finalize() {
if (m_callback != null) {
m_connectivityManager.unregisterNetworkCallback(m_callback);
Expand All @@ -201,6 +205,11 @@ public void finalize() {
m_executor.shutdown();
}

public URL newUrl(String url) throws java.net.MalformedURLException
{
return new URL(url);
}

public native void createClientInstance();

public native void deleteClientInstance();
Expand All @@ -211,12 +220,13 @@ public void finalize() {

public native void onPowerChange(boolean isCharging, boolean isLow);

public native void dispatchCallback(String id, int response, Object[] headers, byte[] body);

public FutureTask<Boolean> createTask(String url, String method, byte[] body, String request_id, int[] header_index,
byte[] header_buffer) {
try {
Request r = new Request(url, method, body, request_id, header_index, header_buffer);
Request r = new Request(this, url, method, body, request_id, header_index, header_buffer);
FutureTask<Boolean> t = new FutureTask<Boolean>(r, true);
m_executor.execute(t);
return t;
} catch (Exception e) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package com.microsoft.applications.events;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import static org.mockito.Mockito.*;
import org.mockito.junit.MockitoJUnitRunner;

import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.BatteryManager;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;

import com.microsoft.applications.events.httpClient;

import static org.junit.Assert.*;

/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(MockitoJUnitRunner.class)
public class EventsUnitTest {
@Mock
Context mockContext;

@Mock
Intent mockIntent;

@Mock
ConnectivityManager mockManager;

@Mock
URL mockUrl;

@Mock
HttpURLConnection mockConnection;

@Mock
OutputStream mockBodyStream;

@Mock
ExecutorService mockExecutor;

class Stubby extends httpClient {

Stubby(Context context) throws java.io.IOException {
super(context);
}

@Override
protected boolean hasConnectivityManager() {
return false;
}

@Override
public void createClientInstance() {

}

@Override
public void deleteClientInstance() {

}

@Override
public void setCacheFilePath(String path) {

}

@Override
public void onCostChange(boolean isMetered) {

}

@Override
public void onPowerChange(boolean isCharging, boolean isLow) {

}

@Override
public void dispatchCallback(String id, int response, Object[] headers, byte[] body) {
assertNotNull(id);
}

@Override
public URL newUrl(String url) throws java.net.MalformedURLException {
return mockUrl;
}

@Override
protected ExecutorService createExecutor()
{
return mockExecutor;
}
}

class StubbyAllow extends Stubby {
StubbyAllow(Context context) throws java.io.IOException {
super(context);
}

@Override
protected boolean hasConnectivityManager() {
return true;
}
}

@Test
public void canInstantiate() throws java.io.IOException {
when(mockContext.registerReceiver(isA(BroadcastReceiver.class), isA(IntentFilter.class))).thenReturn(mockIntent);
Stubby stubs = new Stubby(mockContext);
/* Stubby should not attempt to access the CONNECTIVITY_SERVICE */
verify(mockContext, times(0)).getSystemService(Context.CONNECTIVITY_SERVICE);
verify(mockContext, times(1)).registerReceiver(isA(BroadcastReceiver.class), isA(IntentFilter.class));
verify(mockIntent, times(1)).getIntExtra(BatteryManager.EXTRA_STATUS, -1);
}

@Test
public void canInstantiateWithConnectivity() throws java.io.IOException {
when(mockContext.registerReceiver(isA(BroadcastReceiver.class), isA(IntentFilter.class))).thenReturn(mockIntent);
when(mockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mockManager);
StubbyAllow stubs = new StubbyAllow(mockContext);
verify(mockContext, times(1)).getSystemService(Context.CONNECTIVITY_SERVICE);
verify(mockManager, times(1)).isActiveNetworkMetered();
verify(mockManager, times(1)).registerDefaultNetworkCallback(isA(android.net.ConnectivityManager.NetworkCallback.class));
}

@Test
public void canCreateTask() throws java.io.IOException {
final List<String> someValue = new Vector<String>();
someValue.add("bar");
final Map<String, List<String>> headerMap = new TreeMap<String, List<String>>();
headerMap.put("foo", someValue);
String body_string = "fred";
byte[] body_bytes = body_string.getBytes();
final InputStream bodyStream = new ByteArrayInputStream(body_bytes);

when(mockUrl.openConnection()).thenReturn(mockConnection);
when(mockConnection.getOutputStream()).thenReturn(mockBodyStream);
when(mockConnection.getHeaderFields()).thenReturn(headerMap);
when(mockConnection.getResponseCode()).thenReturn(200);
when(mockConnection.getInputStream()).thenReturn(bodyStream);
when(mockContext.registerReceiver(isA(BroadcastReceiver.class), isA(IntentFilter.class))).thenReturn(mockIntent);

Stubby stubs = new Stubby(mockContext);
final String url = "https://www.contoso.com";
final String method = "POST";
final byte[] body = {0, 1, 2};
final String request_id = "Fred The Request";
final int[] header_index = {4, 3};
final byte[] header_buffer = {0, 1, 2, 3, 7, 8, 9};
FutureTask<Boolean> task = stubs.createTask(
url, method, body, request_id, header_index, header_buffer
);
assertNotNull(task);
task.run();
}
}
Loading