Repository: Azure/azure-functions-java-worker
Pull request #604 (2022-08-03 22:44:19)

Updated java worker to build and run tests on Linux
<!-- Please provide all the information below.  -->

### Updated java worker to build and run tests on Linux
- Removed old maven-surefire-plugin
- Used mockito and updated tests for JavaFunctionBrokerTest 
  - this is done due to the `Surefire Maven plugin: "Corrupted STDOUT by directly writing to native stream in forked JVM` error
- Updated end to end tests to support Linux

### Pull request checklist

* [x] My changes **do not** require documentation changes
  * [ ] Otherwise: Documentation issue linked to PR
* [x] My changes **should not** be added to the release notes for the next release
  * [ ] Otherwise: I've added my notes to `release_notes.md`
* [ ] My changes **do not** need to be backported to a previous version
  * [ ] Otherwise: Backport tracked by issue/PR #issue_or_pr
* [x] I have added all required tests (Unit tests, E2E tests)


Patch:
diff --git a/pom.xml b/pom.xml
index 8826464..1c2fadb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -119,6 +119,7 @@
 		<dependency>
 			<groupId>junit</groupId>
 			<artifactId>junit</artifactId>
+			<version>4.13.1</version>
 			<scope>test</scope>
 		</dependency>
 		<dependency>
@@ -127,9 +128,9 @@
    			<version>5.3.0</version>
 		</dependency>
 		<dependency>
-			<groupId>org.jmockit</groupId>
-			<artifactId>jmockit</artifactId>
-			<version>1.49</version>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-core</artifactId>
+			<version>4.6.1</version>
 			<scope>test</scope>
 		</dependency>
 	</dependencies>
@@ -305,18 +306,6 @@
 					</execution>
 				</executions>
 			</plugin>
-
-			<!-- This plugin helps to config and run unit tests -->
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-surefire-plugin</artifactId>
-				<version>2.22.2</version> <!-- or some other version -->
-				<configuration>
-					<argLine>
-						-javaagent:"${settings.localRepository}"\org\jmockit\jmockit\1.49\jmockit-1.49.jar
-					</argLine>
-				</configuration>
-			</plugin>
 		</plugins>
 	</build>
 </project>


Test patch:
diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FixtureHelpers.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FixtureHelpers.cs
index 3b2450b..262e599 100644
--- a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FixtureHelpers.cs
+++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Fixtures/FixtureHelpers.cs
@@ -4,6 +4,7 @@
 using System;
 using System.Diagnostics;
 using System.IO;
+using System.Runtime.InteropServices;
 
 namespace Azure.Functions.Java.Tests.E2E
 {
@@ -12,14 +13,21 @@ namespace Azure.Functions.Java.Tests.E2E
         public static Process GetFuncHostProcess(bool enableAuth = false)
         {
             var funcHostProcess = new Process();
-            var rootDir = Path.GetFullPath(@"..\..\..\..\..\..");
+            var rootDir = Path.GetFullPath(@"../../../../../..");
 
             funcHostProcess.StartInfo.UseShellExecute = false;
             funcHostProcess.StartInfo.RedirectStandardError = true;
             funcHostProcess.StartInfo.RedirectStandardOutput = true;
             funcHostProcess.StartInfo.CreateNoWindow = true;
-            funcHostProcess.StartInfo.WorkingDirectory = Path.Combine(rootDir, @"endtoendtests\target\azure-functions\azure-functions-java-endtoendtests");
-            funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli\func.exe");
+            funcHostProcess.StartInfo.WorkingDirectory = Path.Combine(rootDir, @"endtoendtests/target/azure-functions/azure-functions-java-endtoendtests");
+            if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func.exe");
+            }
+            else
+            {
+                funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func");
+            }
             funcHostProcess.StartInfo.ArgumentList.Add("start");
             if (enableAuth)
             {
diff --git a/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java b/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java
index 2fb7e43..231c1d6 100644
--- a/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java
+++ b/src/test/java/com/microsoft/azure/functions/worker/broker/JavaFunctionBrokerTest.java
@@ -3,53 +3,59 @@ package com.microsoft.azure.functions.worker.broker;
 import com.microsoft.azure.functions.rpc.messages.InvocationRequest;
 import com.microsoft.azure.functions.rpc.messages.ParameterBinding;
 import com.microsoft.azure.functions.rpc.messages.TypedData;
-import com.microsoft.azure.functions.worker.broker.JavaFunctionBroker;
-import com.microsoft.azure.functions.worker.description.FunctionMethodDescriptor;
 import com.microsoft.azure.functions.worker.reflect.DefaultClassLoaderProvider;
-import mockit.*;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.*;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.when;
 
+@RunWith(MockitoJUnitRunner.class)
 public class JavaFunctionBrokerTest {
+    @Mock InvocationRequest request;
+    @Mock ParameterBinding binding;
+    @Mock TypedData bindingData;
+    @Mock TypedData name;
+    @Mock TypedData query;
+    @Mock TypedData headers;
+    @Mock TypedData sys;
+    @Mock TypedData queueTrigger;
+    @Mock TypedData dequeueCount;
+    @Mock TypedData expirationTime;
+    @Mock TypedData id;
+    @Mock TypedData insertionTime;
+    @Mock TypedData nextVisibleTime;
+    @Mock TypedData popReceipt;
 
     @Test
-    public void getTriggerMetadataMap_success(
-            @Mocked InvocationRequest request,
-            @Mocked ParameterBinding binding,
-            @Mocked TypedData bindingData,
-            @Mocked TypedData name,
-            @Mocked TypedData query,
-            @Mocked TypedData headers,
-            @Mocked TypedData sys
-            ) throws Exception {
-
+    public void getTriggerMetadataMap_success() throws Exception {
         String expectedData = "http {\n  method: \"GET\"\n  url: \"https://localhost:5001/api/HttpExample?name=ushio\"\n  headers {\n    key: \"cache-control\"\n    value: \"max-age=0\"\n  }\n  headers {\n    key: \"connection\"\n    value: \"Keep-Alive\"\n  }\n  headers {\n    key: \"accept\"\n    value: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"\n  }\n  headers {\n    key: \"accept-encoding\"\n    value: \"gzip, deflate, br\"\n  }\n  headers {\n    key: \"accept-language\"\n    value: \"en-US,ja;q=0.8,en-GB;q=0.5,en;q=0.3\"\n  }\n  headers {\n    key: \"host\"\n    value: \"localhost:5001\"\n  }\n  headers {\n    key: \"user-agent\"\n    value: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041\"\n  }\n  headers {\n    key: \"upgrade-insecure-requests\"\n    value: \"1\"\n  }\n  query {\n    key: \"name\"\n    value: \"ushio\"\n  }\n  identities {\n    name_claim_type {\n      value: \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\"\n    }\n    role_claim_type {\n      value: \"http://schemas.microsoft.com/ws/2008/06/identity/claims/role\"\n    }\n  }\n}\n";
         String expectedName = "req";
-        new Expectations() {{
-            bindingData.hasHttp(); result = true;
-            bindingData.getString(); result = expectedData;
-            binding.getName(); result = expectedName;
-            binding.getData(); result = bindingData;
-            request.getInputDataList(); result = Arrays.asList(binding);
-
-            name.getString(); result = "string: \"John\"\n"; minTimes = 0;
-            query.getString(); result = "json: \"{\"name\":\"ushio\"}\""; minTimes = 0;
-            headers.getString(); result = "json: \"{\"Cache-Control\":\"max-age=0\",\"Connection\":\"Keep-Alive\",\"Accept\":\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\"Accept-Encoding\":\"gzip, deflate, br\",\"Accept-Language\":\"en-US,ja;q=0.8,en-GB;q=0.5,en;q=0.3\",\"Host\":\"localhost:5001\",\"User-Agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041\",\"Upgrade-Insecure-Requests\":\"1\"}\""; minTimes = 0;
-            sys.getString(); result = "json: \"{\"MethodName\":\"HttpExample\",\"UtcNow\":\"2020-04-30T15:26:57.281277Z\",\"RandGuid\":\"cd332c4a-df9e-415a-acd4-973994072e46\"}\""; minTimes = 0;
-
-            Map<String,TypedData> triggerMetadata = new HashMap<String, TypedData>();
-            triggerMetadata.put("name", name);
-            triggerMetadata.put("Query", query);
-            triggerMetadata.put("Headers", headers);
-            triggerMetadata.put("sys", sys);
-            request.getTriggerMetadataMap(); result = Collections.unmodifiableMap(triggerMetadata);
-        }};
+
+        when(bindingData.hasHttp()).thenReturn(true);
+        when(bindingData.getString()).thenReturn(expectedData);
+        when(binding.getName()).thenReturn(expectedName);
+        when(binding.getData()).thenReturn(bindingData);
+        when(request.getInputDataList()).thenReturn(Arrays.asList(binding));
+
+        lenient().when(name.getString()).thenReturn("string: \"John\"\n");
+        lenient().when(query.getString()).thenReturn("json: \"{\"name\":\"ushio\"}\"");
+        lenient().when(headers.getString()).thenReturn("json: \"{\"Cache-Control\":\"max-age=0\",\"Connection\":\"Keep-Alive\",\"Accept\":\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\"Accept-Encoding\":\"gzip, deflate, br\",\"Accept-Language\":\"en-US,ja;q=0.8,en-GB;q=0.5,en;q=0.3\",\"Host\":\"localhost:5001\",\"User-Agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041\",\"Upgrade-Insecure-Requests\":\"1\"}\"");
+        lenient().when(sys.getString()).thenReturn("json: \"{\"MethodName\":\"HttpExample\",\"UtcNow\":\"2020-04-30T15:26:57.281277Z\",\"RandGuid\":\"cd332c4a-df9e-415a-acd4-973994072e46\"}\"");
+
+        Map<String,TypedData> triggerMetadata = new HashMap<String, TypedData>();
+        triggerMetadata.put("name", name);
+        triggerMetadata.put("Query", query);
+        triggerMetadata.put("Headers", headers);
+        triggerMetadata.put("sys", sys);
+        when(request.getTriggerMetadataMap()).thenReturn(Collections.unmodifiableMap(triggerMetadata));
 
         JavaFunctionBroker broker = new JavaFunctionBroker(new DefaultClassLoaderProvider());
         Map<String, TypedData> actualTriggerMetadata = broker.getTriggerMetadataMap(request);
@@ -60,49 +66,35 @@ public class JavaFunctionBrokerTest {
     }
 
     @Test
-    public void getTriggerMetadataMap_ignored(
-            @Mocked InvocationRequest request,
-            @Mocked ParameterBinding binding,
-            @Mocked TypedData bindingData,
-            @Mocked TypedData queueTrigger,
-            @Mocked TypedData dequeueCount,
-            @Mocked TypedData expirationTime,
-            @Mocked TypedData id,
-            @Mocked TypedData insertionTime,
-            @Mocked TypedData nextVisibleTime,
-            @Mocked TypedData popReceipt,
-            @Mocked TypedData sys
-    ) throws Exception {
-
+    public void getTriggerMetadataMap_ignored() throws Exception {
         String data = "string: \"hello queue\"\n";
         String name = "msg";
-        new Expectations() {{
-            bindingData.hasHttp(); result = false;
-            bindingData.getString(); result = data; minTimes = 0;
-            binding.getName(); result = name; minTimes = 0;
-            binding.getData(); result = bindingData; minTimes = 0;
-            request.getInputDataList(); result = Arrays.asList(binding);
-
-            queueTrigger.getString(); result = "string: \"hello queue\"\n"; minTimes = 0;
-            dequeueCount.getString(); result = "json: \"1\"\n"; minTimes = 0;
-            expirationTime.getString(); result = "json: \"\"2020-05-08T17:47:22+00:00\"\n"; minTimes = 0;
-            id.getString(); result = "string: \"e4f4a332-df80-41a1-8ecd-c2f7fba91f28\"\n"; minTimes = 0;
-            insertionTime.getString(); result = "json: \"\"2020-05-01T17:47:22+00:00\"\n"; minTimes = 0;
-            nextVisibleTime.getString(); result = "json: \"\"2020-05-01T17:57:22+00:00\"\n"; minTimes = 0;
-            popReceipt.getString(); result = "string: \"oJCJGfnt1wgBAAAA\"\n"; minTimes = 0;
-            sys.getString(); result =  "json: \"{\"MethodName\":\"QueueProcessor\",\"UtcNow\":\"2020-05-01T17:47:29.2664174Z\",\"RandGuid\":\"7f67ac1c-b7b0-43f5-a51a-73af321d7d9f\"}\n"; minTimes = 0;
-
-            Map<String,TypedData> triggerMetadata = new HashMap<String, TypedData>();
-            triggerMetadata.put("QueueTrigger", queueTrigger);
-            triggerMetadata.put("DequeueCount", dequeueCount);
-            triggerMetadata.put("ExpirationTime", expirationTime);
-            triggerMetadata.put("Id", id);
-            triggerMetadata.put("InsertionTime", insertionTime);
-            triggerMetadata.put("NextVisibleTime", nextVisibleTime);
-            triggerMetadata.put("PopReceipt", popReceipt);
-            triggerMetadata.put("sys", sys);
-            request.getTriggerMetadataMap(); result = Collections.unmodifiableMap(triggerMetadata);
-        }};
+
+        when(bindingData.hasHttp()).thenReturn(false);
+        lenient().when(bindingData.getString()).thenReturn(data);
+        lenient().when(binding.getName()).thenReturn(name);
+        when(binding.getData()).thenReturn(bindingData);
+        when(request.getInputDataList()).thenReturn(Arrays.asList(binding));
+
+        lenient().when(queueTrigger.getString()).thenReturn("string: \"hello queue\"\n");
+        lenient().when(dequeueCount.getString()).thenReturn("json: \"1\"\n");
+        lenient().when(expirationTime.getString()).thenReturn("json: \"\"2020-05-08T17:47:22+00:00\"\n");
+        lenient().when(id.getString()).thenReturn("string: \"e4f4a332-df80-41a1-8ecd-c2f7fba91f28\"\n");
+        lenient().when(insertionTime.getString()).thenReturn("json: \"\"2020-05-01T17:47:22+00:00\"\n");
+        lenient().when(nextVisibleTime.getString()).thenReturn("json: \"\"2020-05-01T17:57:22+00:00\"\n");
+        lenient().when(popReceipt.getString()).thenReturn("string: \"oJCJGfnt1wgBAAAA\"\n");
+        lenient().when(sys.getString()).thenReturn( "json: \"{\"MethodName\":\"QueueProcessor\",\"UtcNow\":\"2020-05-01T17:47:29.2664174Z\",\"RandGuid\":\"7f67ac1c-b7b0-43f5-a51a-73af321d7d9f\"}\n");
+
+        Map<String,TypedData> triggerMetadata = new HashMap<String, TypedData>();
+        triggerMetadata.put("QueueTrigger", queueTrigger);
+        triggerMetadata.put("DequeueCount", dequeueCount);
+        triggerMetadata.put("ExpirationTime", expirationTime);
+        triggerMetadata.put("Id", id);
+        triggerMetadata.put("InsertionTime", insertionTime);
+        triggerMetadata.put("NextVisibleTime", nextVisibleTime);
+        triggerMetadata.put("PopReceipt", popReceipt);
+        triggerMetadata.put("sys", sys);
+        when(request.getTriggerMetadataMap()).thenReturn(Collections.unmodifiableMap(triggerMetadata));
 
         int expectedCount = request.getTriggerMetadataMap().size();
         JavaFunctionBroker broker = new JavaFunctionBroker(new DefaultClassLoaderProvider());
diff --git a/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..ca6ee9c
--- /dev/null
+++ b/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1,1 @@
+mock-maker-inline
\ No newline at end of file