I've been trying to use the Android NDK's AssetManager class in my Flutter app, that works with Google Oboe, to access to audio files. Following this example in the Oboe repository, I learned that they obtain the AssetManager from Java like this:
JNIEXPORT void JNICALL
Java_com_google_oboe_sample_rhythmgame_MainActivity_native_1onStart(JNIEnv *env, jobject instance,
jobject jAssetManager) {
AAssetManager *assetManager = AAssetManager_fromJava(env, jAssetManager);
if (assetManager == nullptr) {
LOGE("Could not obtain the AAssetManager");
return;
}
game = std::make_unique<Game>(*assetManager);
game->start();
}
Basically with the argument jAssetManager they pass from Java to the C++ functions, through the JNI interface. Now I'm not working with JNI because I'm using Flutter and Dart, and the way in Dart for communicating with C++ functions is through dart:ffi, but since the only way I can create an AssetManager is with AAssetManager_fromJava(env, jAssetManager), I need those two arguments that I can't find a way to replace with Flutter and Dart.
I did some research and when I created the Flutter FFI plugin, apparently the Dart code communicates with Kotlin code, which then calls the native C++ functions.
Here's my C++ function:
EXTERNC void *engine_create(void) {
AAssetManager *assetManager = AAssetManager_fromJava(env, jAssetManager); // ERROR: How do I get these?
if (assetManager == nullptr) {
LOGE("Could not obtain the AAssetManager");
return nullptr;
}
return new DSPAudioEngine(*assetManager);
}
Here's the Dart wrapper for that function:
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';
typedef oboe_engine_init = Pointer<Void> Function();
typedef OboeEngineInit = Pointer<Void> Function();
class FfiGoogleOboe {
static const MethodChannel _channel =
const MethodChannel('ffi_google_oboe');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static FfiGoogleOboe _instance;
factory FfiGoogleOboe() {
if (_instance == null) {
_instance = FfiGoogleOboe._();
}
return _instance;
}
OboeEngineInit _engineInit;
FfiGoogleOboe._() {
final oboeLib = DynamicLibrary.open('libffi_google_oboe.so');
_engineInit = oboeLib
.lookup<NativeFunction<oboe_engine_init>>('engine_create')
.asFunction();
}
}
And here's the Kotlin code I found in the FFI plugin implementation:
package g1_assd_2020.ffi_google_oboe
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import android.content.res.AssetManager
/** FfiGoogleOboePlugin */
public class FfiGoogleOboePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "ffi_google_oboe")
channel.setMethodCallHandler(this);
}
// This static function is optional and equivalent to onAttachedToEngine. It supports the old
// pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
// plugin registration via this function while apps migrate to use the new Android APIs
// post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
//
// It is encouraged to share logic between onAttachedToEngine and registerWith to keep
// them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
// depending on the user's project. onAttachedToEngine or registerWith must both be defined
// in the same class.
companion object {
#JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "ffi_google_oboe")
channel.setMethodCallHandler(FfiGoogleOboePlugin())
}
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
Finally, here's how the people from Oboe handle it using JNI and Java:
package com.google.oboe.sample.rhythmgame;
import android.content.Context;
import android.content.res.AssetManager;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setDefaultStreamValues(this);
}
protected void onResume(){
super.onResume();
native_onStart(getAssets());
}
protected void onPause(){
super.onPause();
native_onStop();
}
static void setDefaultStreamValues(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int defaultSampleRate = Integer.parseInt(sampleRateStr);
String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst);
}
}
private native void native_onStart(AssetManager assetManager);
private native void native_onStop();
private static native void native_setDefaultStreamValues(int defaultSampleRate,
int defaultFramesPerBurst);
}
Basically, you need to pass the AssetManager reference from your plugin's Kotlin file to the C++ library. This answer explains how to make the Kotlin file call C++ code: Android: How to call ndk function from Kotlin?
You'll want to use a methodChannel call to trigger this. You can get the AssetManager reference in the onAttachedToEngine method from flutterPluginBinding.applicationContext.assets.
Here's an example Flutter plugin that reads an asset in a C++ library:
https://github.com/mikeperri/ndk_asset_manager_example/commit/533d28b33c1d22f89028f89691f78e907bf19db3
Related
I am trying to store a geo_point type data in datastore via GCP Java client library. I figured out how to do for a Date type data, but could not get a clue which GeoPoint class I use for this.
import com.google.datastore.v1.Entity;
import static com.google.datastore.v1.client.DatastoreHelper.makeValue;
import java.util.Date;
...
public class WriteToDatastoreFromTwitter {
private static Value dValue(Date k) {
return makeValue(k).setExcludeFromIndexes(true).build();
}
public static void main(String[] args) throws TwitterException {
final Builder builder = Entity.newBuilder().
setKey(key).
putProperties("timestamp", dValue(tweet.getCreatedAt()));
// How can I add a `geo_point` data?
I am simply not sure if I should use classes outside of the datastore package, such as this: https://cloud.google.com/appengine/docs/standard/java/javadoc/com/google/appengine/api/search/GeoPoint
I figured out by myself. There is a class LatLng in a dependent package com.google.type to the datastore package and I could use this to successfully store geo_point data. Here's how you initialize the object:
import com.google.type.LatLng;
...
LatLng x = LatLng.
newBuilder().
setLatitude(loc.getLatitude()).
setLongitude(loc.getLongitude()).
build();
and in my case, I stored it by doing
private static Value gValue(LatLng k) {
return makeValue(k).setExcludeFromIndexes(true).build();
}
builder.putProperties("geo_point", gValue(x));
The below test throws java.lang.IllegalStateException: no last call on a mock available when I don't extend from the PowerMockTestCase.
The error disappears as soon as I extend from PowerMockTestCase. Why exactly is this happening?
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMock;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
#PrepareForTest({ IdGenerator.class, ServiceRegistartor.class })
public class SnippetTest extends PowerMockTestCase{
#org.testng.annotations.Test
public void testRegisterService() throws Exception {
long expectedId = 42;
// We create a new instance of test class under test as usually.
ServiceRegistartor tested = new ServiceRegistartor();
// This is the way to tell PowerMock to mock all static methods of a
// given class
PowerMock.mockStatic(IdGenerator.class);
/*
* The static method call to IdGenerator.generateNewId() expectation.
* This is why we need PowerMock.
*/
EasyMock.expect(IdGenerator.generateNewId()).andReturn(expectedId).once();
// Note how we replay the class, not the instance!
PowerMock.replay(IdGenerator.class);
long actualId = tested.registerService(new Object());
// Note how we verify the class, not the instance!
PowerMock.verify(IdGenerator.class);
// Assert that the ID is correct
assertEquals(expectedId, actualId);
}
}
While using PowerMock for static mocking, there is a class level instrumentation happening to make your mocking work. PowerMockTestCase class has a code (method beforePowerMockTestClass()) to switch your regular class loader to powermock class loader which orchestrates mocking injection. Hence you need to extend this class for static mock to work.
You need to have the PowerMock class-loaders configured so that the static classes can be intercepted (defined using the #PrepareForTest annotation).
You don't have to extend from PowerMockTestCase. For most cases you can also configure TestNG with a PowerMockObjectFactory instead:
#PrepareForTest({ IdGenerator.class, ServiceRegistartor.class })
public class SnippetTest {
#ObjectFactory
public IObjectFactory objectFactory() {
return new PowerMockObjectFactory();
}
#org.testng.annotations.Test
public void testRegisterService() throws Exception {
...
}
}
I am a Dagger newbie.
TL;DR:
If an Android Service has any fields injected into it using Dagger, then in order to actually perform the injection, I need to have an instance of that Service.
In Robolectric tests, this corresponds to MyService service = Robolectric.buildService(MyService.class).get(). And then, objectGraph.inject(service);
However, rest of the code that actually starts MyService still uses context.startService(context, MyService.class);.
Question: What is the idiomatic way in Dagger to address this mismatch?
Let's say I have a Service as follows:
public class MyService {
#Inject Parser parser;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String data = intent.getStringExtra("data_to_be_parsed");
parser.parse(data);
}
}
Elsewhere in my code, I have an ApiClient class that does this:
public class ApiClient{
public static void parseInBackground(Context context, String data){
//This service does not have its fields injected
context.startService(new Intent(context, MyService.class).putExtra("data_to_be_parsed", data));
}
}
That parseInBackground method will be called from an Activity in response to user interaction.
Now, I'm following TDD and hence, I haven't yet written the Application Module for this. Here's the test module:
#Module(injects = MyService.class)
public class TestModule {
#Provides #Singleton Parser provideParser(){
return new MockParser();
}
}
And finally, the test case:
#RunWith(Robolectric.class)
public class ApiTest {
#Test
public void parseInBackground_ParsesCorrectly(){
//This service has its fields injected
MyService service = Robolectric.buildService(MyService.class).get();
ObjectGraph.create(new TestModule()).inject(service);
ApiClient.parseInBackground(Robolectric.application, "<user><name>droid</name></user>");
//Asserts here
}
}
As you can see, in the test, I retrieve an instance of the service and then inject the MockParser into it. However, the ApiClient class directly starts the service using an Intent. I don't have a chance to perform the injection.
I am aware that I can have MyService perform an injection on itself:
public void onCreate(){
ObjectGraph.create(new TestModule()).inject(this);
}
But then, I am hardcoding the TestModule here.
Is there an existing idiom in Dagger to set up dependencies for such situations?
It's the wrong way to hardcode your modules either in tests or in services. Better approach is to perform creation via your custom Application object which in turn will hold singleton ObjectGraph object. For example:
// in MyService class
#Override public void onCreate() {
super.onCreate();
MyApp.from(context).inject(this);
}
// in MyApp class
public static MyApp from(Context context) {
return (MyApp) context.getApplicationContext();
}
//...
private ObjectGraph objectGraph;
#Override public void onCreate() {
// Perform Injection
objectGraph = ObjectGraph.create(getModules());
objectGraph.inject(this);
}
public void inject(Object object) {
objectGraph.inject(object);
}
protected Object[] getModules() {
// return concrete modules based on build type or any other conditions.
}
Alternatively, you can refactor last method out into separate class and make different implementations for different flavors or build types. Also you may want to set overrides=true in your TestModule's annotation.
I'm writing an adapter for Enterprise Library Logging and as per our coding standards, need a way to unit test.
The interface and implementation is simple, but I need a way to check to see that Logger.Write has been called with certain parameters on the log entry. Unfortunately the Logger.Writer does not have a setter, and I don't have any tools to mock a static class.
I'd like to be able to, for example, assert the event ID or the severity for the underlying writer when an adapter method is called.
You can implement a custom tracelistener, and use it as your unit test trace listener:
[ConfigurationElementType(typeof(CustomTraceListenerData))]
public class StubTraceListener : CustomTraceListener
{
private readonly static List<LogEntry> logEntries_ =
new List<LogEntry>();
private readonly static List<string> logMessages_ =
new List<string>();
public override void Write(string message)
{
StubTraceListener.logMessages_.Add(message);
}
public override void WriteLine(string message)
{
StubTraceListener.logMessages_.Add(message);
}
public override void TraceData(TraceEventCache eventCache,
string source, TraceEventType eventType, int id,
object data)
{
LogEntry le = data as LogEntry;
if (le != null)
{
StubTraceListener.logEntries_.Add(le);
if (this.Formatter != null)
{
this.Write(this.Formatter.Format(le));
return;
}
}
base.TraceData(eventCache, source, eventType, id, data);
}
internal static IList<string> GetLogMessages()
{
return new ReadOnlyCollection<string>
(StubTraceListener.logMessages_);
}
internal static IList<LogEntry> GetLogEntries()
{
return new ReadOnlyCollection<LogEntry>
(StubTraceListener.logEntries_);
}
internal static void Reset()
{
StubTraceListener.logEntries_.Clear();
StubTraceListener.logMessages_.Clear();
}
}
Then, in the test project's App.config file include the trace listener like this:
<listeners>
<add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=6.0.0.0,Culture=neutral"
type="Path.To.Your.Project.Folder.StubTraceListener, Your.Test.Project.Name" name="MockTraceListener" />
</listeners>
Then you can easily create the writer as you normally do:
Logger.SetLogWriter(new LogWriterFactory().Create(), false);
I Would like to test my Hub in SignalR, what is the best approach?
Possible solutions I have thought about so far:
Create a testable Hub
Abstract logic to separate class
Selenium (would like to test smaller units)
Or is it some SignalR testing features have overlooked
Currently using SignalR 0.4, and NUnit as the testing framework.
This link shows how to unit test SignalR hub methods using Moq. You mock up the respository, clients, context, and the caller. Here's the code from the site, I made some minor changes to make it work with the latest SignalR:
public class TestableChatHub : ChatHub
{
public Mock<IChatRepository> MockChatRepository { get; private set; }
public TestableChatHub(Mock<IChatRepository> mockChatRepository)
: base(mockChatRepository.Object)
{
const string connectionId = "1234";
const string hubName = "Chat";
var mockConnection = new Mock<IConnection>();
var mockUser = new Mock<IPrincipal>();
var mockCookies = new Mock<IRequestCookieCollection>();
var mockRequest = new Mock<IRequest>();
mockRequest.Setup(r => r.User).Returns(mockUser.Object);
mockRequest.Setup(r => r.Cookies).Returns(mockCookies.Object);
Clients = new ClientProxy(mockConnection.Object, hubName);
Context = new HubCallerContext(mockRequest.Object, connectionId);
var trackingDictionary = new TrackingDictionary();
Caller = new StatefulSignalProxy(
mockConnection.Object, connectionId, hubName, trackingDictionary);
}
}
Then the site shows that you can use this testable hub to write unit tests:
[TestClass]
public class ChatHubTests
{
private TestableChatHub _hub;
public void SetUpTests()
{
_hub = GetTestableChatHub();
}
[Test]
public void ExampleTest()
{
SetUpTests();
const string message = "test";
const string connectionId = "1234";
var result = _hub.Send(message);
_hub.MockChatRepository.Verify(r => r.SaveMessage(message, connectionId));
Assert.IsTrue(result);
}
private TestableChatHub GetTestableChatHub()
{
var mockRepository = new Mock<IChatRepository>();
mockRepository.Setup(m => m.SaveMessage(
It.IsAny<string>(), It.IsAny<string())).Returns(true);
return new TestableChatHub(mockRepository);
}
}
It's quite simple to create to unit test SignalR hubs using a couple of neat tricks. One thing to note is that SignalR uses dynamic classes which might not be supported by your mocking framework (I use NSubstitute).
public class ProjectsHub: Hub
{
public void AddProject(string id)
{
Clients.All.AddProject(id);
}
}
[TestFixture]
public class ProjectsHubTests
{
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals
{
void AddProject(string id);
}
[Test]
public void AddProject_Broadcasts()
{
// Arrange
ProjectsHub hub = new ProjectsHub();
IHubCallerConnectionContext clients =
Substitute.For<IHubCallerConnectionContext>();
ISignals signals = Substitute.For<ISignals>();
SubstituteExtensions.Returns(clients.All, signals);
hub.Clients = clients;
// Act
hub.AddProject("id");
// Assert
signals.Received(1).AddProject("id");
}
}
Rewriting this to use e.g. Moq should be pretty simple.
This question is from a while ago, but I'll do my best to answer anyway.
If you have a lot of logic in your actual hub class, it would certainly make sense to abstract the logic to a separate class. I did the same for my SignalR-powered multiplayer demo. The only behaviour that should go in your hub class itself is the one related to messaging. All further action should be delegated.
Note: This is very much like the guidelines for controller design in ASP .NET MVC: Keep your controllers small and delegate the real work.
If you want integration tests with SignalR actually doing some work, selenium webdriver would be a good option. But you will probably need to do some tweaking to get the SignalR messaging working perfectly in the context of the tests. Do a google search for "signalr selenium" (without the quotes) to get started on the right track.
Some blogposts about automated tests for SignalR => here and here
With the SignalR 2.0 you can do it this way:
// Arrange
var hub = new CodeInteractivePreviewHub();
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
hub.Clients = mockClients.Object;
dynamic all = new ExpandoObject();
mockClients.Setup(m => m.All).Returns((ExpandoObject)all);
// Act
var allSourceCodes = hub.InitiateCommunication(); //Change this line to your Hub's method
// Assert
Assert.IsNotNull(allSourceCodes);
This is modified version of Iarsm's answer, to work with XUnit and MOQ.
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Moq;
using Xunit;
namespace TestLibrary {
public class ProjectsHub : Hub {
public void AddProject(string id) {
Clients.All.AddProject(id);
}
}
public class ProjectsHubTests {
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals {
void AddProject(string id);
}
[Fact]
public void AddProject_Broadcasts() {
// Arrange
ProjectsHub hub = new ProjectsHub();
var clients = new Mock<IHubCallerConnectionContext<dynamic>>();
var signals = new Mock<ISignals>();
hub.Clients = clients.Object;
signals.Setup(m => m.AddProject(It.Is<string>(s => s == "id"))).Verifiable();
clients.Setup(m => m.All).Returns(signals.Object);
// Act
hub.AddProject("id");
// Assert
signals.VerifyAll();
}
}
}
My interface was out of the test project, but here's how I did it using NUnit and Moq.
using Microsoft.AspNetCore.SignalR;
using Moq;
using NUnit.Framework;
namespace TestLibrary
{
// Operations that clients might receive
// This interface is in place in order to mock the
// dynamic object used in SignalR
public interface ISignals
{
void AddProject(string id);
}
public class ProjectsHub : Hub<ISignals>
{
public void AddProject(string id)
{
Clients.All.AddProject(id);
}
}
public class ProjectsHubTests
{
[Test]
public void AddProject_Broadcasts()
{
// Arrange
ProjectsHub hub = new ProjectsHub();
var clients = new Mock<IHubCallerClients<ISignals>>();
var signals = new Mock<ISignals>();
hub.Clients = clients.Object;
signals.Setup(m => m.AddProject(It.Is<string>(s => s == "id"))).Verifiable();
clients.Setup(m => m.All).Returns(signals.Object);
// Act
hub.AddProject("id");
// Assert
signals.VerifyAll();
}
}
}