Skip to content

Commit

Permalink
Prepare release 6.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Prashant Ramcharan committed Feb 17, 2024
1 parent b3a40a5 commit 6a38cbe
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 39 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
CHANGES IN VERSION 6.11.0
=================================
* [NEW] Add new Courgette option `mobileDeviceType` to support running tests that requires simulators and real devices in the same run.
* [NEW] Add new Courgette option `realMobileDeviceTag` to allow Courgette to allocate real devices to tests with a matching tag.
* [MAINTENANCE] Upgrade to Cucumber version 7.15.0

CHANGES IN VERSION 6.10.0
=================================
* [MAINTENANCE] Upgrade to Cucumber version 7.12.1
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Prashant Ramcharan
Copyright (c) 2024 Prashant Ramcharan

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ Courgette-JVM is an extension of Cucumber-JVM with added capabilities to **run c
<dependency>
<groupId>io.github.prashant-ramcharan</groupId>
<artifactId>courgette-jvm</artifactId>
<version>6.10.0</version>
<version>6.11.0</version>
</dependency>
````

#### Gradle
````gradle
implementation group: 'io.github.prashant-ramcharan', name: 'courgette-jvm', version: '6.10.0'
implementation group: 'io.github.prashant-ramcharan', name: 'courgette-jvm', version: '6.11.0'
````

#### Included Cucumber Dependencies
* cucumber-core 7.12.1
* cucumber-java 7.12.1
* cucumber-java8 7.12.1
* cucumber-junit 7.12.1
* cucumber-testng 7.12.1
* cucumber-core 7.15.0
* cucumber-java 7.15.0
* cucumber-java8 7.15.0
* cucumber-junit 7.15.0
* cucumber-testng 7.15.0


## Usage
Expand Down Expand Up @@ -122,6 +122,13 @@ Courgette-JVM supports JUnit and TestNG to run cucumber features and scenarios i
* **mobileDevice**: The devices that Courgette will use to track and allocate for parallel mobile tests.
* This option is required when using the `CourgettePlugin.MOBILE_DEVICE_ALLOCATOR` plugin.

* **mobileDeviceType**: The mobile device types used for device allocation. This can be one of:
* _MobileDeviceType.SIMULATOR: Only simulator device names._
* _MobileDeviceType.REAL_DEVICE: Only real devices names, must match format `deviceName:deviceUUID`_
* _MobileDeviceType.SIMULATOR_AND_REAL_DEVICE: Mixture of simulator and real device names._

* **realMobileDeviceTag**: If set, Courgette will allocate a real mobile device for tests matching any one of the provided tags. To use this option, you must also specify mobileDevice as `MobileDeviceType.REAL_DEVICE` or `MobileDeviceType.SIMULATOR_AND_REAL_DEVICE`

* **fixedThreadDelay**: A fixed time in milliseconds that Courgette will pause before the start of each feature or scenario.

* **randomThreadDelay**: A random time in milliseconds that Courgette will pause before the start of each feature or scenario. Courgette will automatically set a random time between 0 and this value.
Expand Down Expand Up @@ -453,6 +460,7 @@ Courgette provides a mobile device allocator to allocate and keep track of devic
@CourgetteOptions(
...
plugin = { CourgettePlugin.MOBILE_DEVICE_ALLOCATOR },
mobileDeviceType = MobileDeviceType.SIMULATOR,
mobileDevice = {
"iPhone 8",
"iPhone 12",
Expand All @@ -468,6 +476,7 @@ The Courgette mobile device allocator plugin will:
* Create a pool of devices based on `mobileDevice` and will automatically allocate a randomly selected available device for each parallel test.
* Determine the optimal parallel threads based on the sum of devices defined in `mobileDevice`. The sum of `mobileDevice` will take precedence over `threads` defined in the Courgette runner.
* Expose the device name, parallel port and uuid (_if provided_) during the runtime of each parallel test.
* If mobileDeviceType is `SIMULATOR_AND_REAL_DEVICE` then Courgette will allocate a real device for tests tagged with any matching tag defined in Courgette option `realMobileDeviceTag` and allocate a simulator for all other tests.

Notes:

Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'io.github.prashant-ramcharan'
version '6.10.0'
version '6.11.0'

apply plugin: 'java'
apply plugin: 'maven-publish'
Expand All @@ -10,7 +10,7 @@ compileJava {
}

ext {
cucumberVersion = '7.12.1'
cucumberVersion = '7.15.0'
testNGVersion = '7.1.0'
jacksonVersion = '2.13.3'
httpcomponentsVersion = '4.5.13'
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/courgette/api/CourgetteOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,21 @@
*/
CourgetteEvent[] slackEventSubscription() default {};

/**
* @return the mobile device type
*/
MobileDeviceType mobileDeviceType() default MobileDeviceType.SIMULATOR;

/**
* @return the mobile devices for device allocation
*/
String[] mobileDevice() default {};

/**
* @return the matching Cucumber tags for real mobile device allocation
*/
String[] realMobileDeviceTag() default {};

/**
* @return the time in milliseconds for the fixed thread delay
*/
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/courgette/api/MobileDeviceType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package courgette.api;

public enum MobileDeviceType {
SIMULATOR,
REAL_DEVICE,
SIMULATOR_AND_REAL_DEVICE
}
11 changes: 8 additions & 3 deletions src/main/java/courgette/runtime/CourgetteFeatureRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@
import static courgette.runtime.utils.SystemPropertyUtils.splitAndAddPropertyToList;

public class CourgetteFeatureRunner {
private final CourgetteRunnerInfo runnerInfo;
private final Map<String, List<String>> runnerArgs;
private final CourgetteProperties courgetteProperties;
private final CourgettePluginService courgettePluginService;

CourgetteFeatureRunner(Map<String, List<String>> runnerArgs, CourgetteProperties courgetteProperties, CourgettePluginService courgettePluginService) {
CourgetteFeatureRunner(CourgetteRunnerInfo runnerInfo,
Map<String, List<String>> runnerArgs,
CourgetteProperties courgetteProperties,
CourgettePluginService courgettePluginService) {
this.runnerInfo = runnerInfo;
this.runnerArgs = runnerArgs;
this.courgetteProperties = courgetteProperties;
this.courgettePluginService = courgettePluginService;
Expand Down Expand Up @@ -98,10 +103,10 @@ private void addCucumberSystemProperties(final List<String> systemPropertyList)

private void addCourgetteMobileDeviceAllocatorProperties(final List<String> systemPropertyList) {
if (courgetteProperties.isMobileDeviceAllocationPluginEnabled()) {
device = courgettePluginService.getCourgetteMobileDeviceAllocatorService().allocateDevice();
device = courgettePluginService.getCourgetteMobileDeviceAllocatorService().allocateDevice(runnerInfo.getDeviceType());
systemPropertyList.add(String.format("-D%s=%s", CourgetteSystemProperty.DEVICE_NAME_SYSTEM_PROPERTY, device.getDeviceName()));
systemPropertyList.add(String.format("-D%s=%s", CourgetteSystemProperty.PARALLEL_PORT_SYSTEM_PROPERTY, device.getParallelPort()));
if (device.getUdid() != null) {
if (runnerInfo.getDeviceType().equals(DeviceType.REAL_DEVICE)) {
systemPropertyList.add(String.format("-D%s=%s", CourgetteSystemProperty.UDID_SYSTEM_PROPERTY, device.getUdid()));
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/courgette/runtime/CourgetteMobileDevice.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

public class CourgetteMobileDevice {
private final String deviceName;
private final DeviceType deviceType;
private final String udid;
private final int parallelPort;

public CourgetteMobileDevice(String deviceName, String udid, int parallelPort) {
public CourgetteMobileDevice(String deviceName, DeviceType deviceType, String udid, int parallelPort) {
this.deviceName = deviceName;
this.deviceType = deviceType;
this.udid = udid;
this.parallelPort = parallelPort;
}
Expand All @@ -15,6 +17,10 @@ public String getDeviceName() {
return deviceName;
}

public DeviceType getDeviceType() {
return deviceType;
}

public String getUdid() {
return udid;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ public CourgetteMobileDeviceAllocatorService(final String[] devices) {
availableDevices.addAll(createCourgetteMobileDevices(devices));
}

public synchronized CourgetteMobileDevice allocateDevice() {
final List<CourgetteMobileDevice> devices = availableDevices
public synchronized CourgetteMobileDevice allocateDevice(final DeviceType deviceType) {
List<CourgetteMobileDevice> devices = availableDevices
.stream()
.filter(device -> device.getDeviceType().equals(deviceType))
.filter(device -> !unavailableDevices.contains(device))
.collect(Collectors.toList());

Expand All @@ -47,9 +48,9 @@ private List<CourgetteMobileDevice> createCourgetteMobileDevices(final String[]
String deviceName = deviceIdentifier.get(0);

if (deviceIdentifier.size() > 1) {
courgetteMobileDevices.add(new CourgetteMobileDevice(deviceName, deviceIdentifier.get(1), getParallelPort()));
courgetteMobileDevices.add(new CourgetteMobileDevice(deviceName, DeviceType.REAL_DEVICE, deviceIdentifier.get(1), getParallelPort()));
} else {
courgetteMobileDevices.add(new CourgetteMobileDevice(deviceName, null, getParallelPort()));
courgetteMobileDevices.add(new CourgetteMobileDevice(deviceName, DeviceType.SIMULATOR, null, getParallelPort()));
}
});
return courgetteMobileDevices;
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/courgette/runtime/CourgetteProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import courgette.api.CourgettePlugin;
import courgette.api.CourgetteRunLevel;
import courgette.api.HtmlReport;
import courgette.api.MobileDeviceType;
import courgette.runtime.utils.SystemPropertyUtils;

import java.time.Instant;
Expand Down Expand Up @@ -41,10 +42,26 @@ public Integer getMaxThreads() {
return maxThreads;
}

public int getMaxThreadsFromMobileDevices() {
public int getMaxThreadsFromMobileDevices(final DeviceType deviceType) {
if (deviceType.equals(DeviceType.SIMULATOR)) {
return getSimulatorDeviceCount();
}
return getRealDeviceCount();
}

public int getSimulatorDeviceCount() {
return Arrays.stream(courgetteOptions.mobileDevice())
.distinct()
.map(device -> device.toLowerCase().trim())
.filter(device -> device.split(":").length == 1)
.collect(Collectors.toSet()).size();
}

public int getRealDeviceCount() {
return Arrays.stream(courgetteOptions.mobileDevice())
.distinct()
.map(device -> device.toLowerCase().trim())
.filter(device -> device.split(":").length == 2)
.collect(Collectors.toSet()).size();
}

Expand Down Expand Up @@ -84,6 +101,10 @@ public boolean useCustomClasspath() {
return courgetteOptions.classPath().length > 0;
}

public boolean isMultipleMobileDeviceTypes() {
return courgetteOptions.mobileDeviceType().equals(MobileDeviceType.SIMULATOR_AND_REAL_DEVICE);
}

public CourgetteSlackOptions slackOptions() {
return new CourgetteSlackOptions(courgetteOptions.slackWebhookUrl(),
Arrays.asList(courgetteOptions.slackChannel()),
Expand Down
44 changes: 36 additions & 8 deletions src/main/java/courgette/runtime/CourgetteRunOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import courgette.api.CourgetteTestOutput;
import courgette.api.CucumberOptions;
import courgette.api.HtmlReport;
import courgette.api.MobileDeviceType;
import courgette.integration.reportportal.ReportPortalProperties;
import courgette.runtime.event.CourgetteEvent;
import courgette.runtime.utils.FileUtils;
Expand All @@ -14,6 +15,7 @@
import java.io.File;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.stream.Collectors;

public class CourgetteRunOptions implements CourgetteOptions {
Expand Down Expand Up @@ -120,11 +122,21 @@ public CourgetteEvent[] slackEventSubscription() {
return courgetteOptions.slackEventSubscription();
}

@Override
public MobileDeviceType mobileDeviceType() {
return courgetteOptions.mobileDeviceType();
}

@Override
public String[] mobileDevice() {
return SystemPropertyUtils.getStringArrayProperty(CourgetteSystemProperty.MOBILE_DEVICE, courgetteOptions.mobileDevice());
}

@Override
public String[] realMobileDeviceTag() {
return SystemPropertyUtils.getStringArrayProperty(CourgetteSystemProperty.REAL_MOBILE_DEVICE_TAG, courgetteOptions.realMobileDeviceTag());
}

@Override
public int fixedThreadDelay() {
return SystemPropertyUtils.getIntProperty(CourgetteSystemProperty.FIXED_THREAD_DELAY, courgetteOptions.fixedThreadDelay());
Expand Down Expand Up @@ -169,16 +181,32 @@ private void validateReportPortalPlugin() {

private void validateMobileDeviceAllocatorPlugin() {
if (Arrays.stream(plugin()).anyMatch(plugin -> plugin.equalsIgnoreCase(CourgettePlugin.MOBILE_DEVICE_ALLOCATOR))) {
String[] mobileDevice = mobileDevice();
if (mobileDevice.length == 0 ||
Arrays.stream(mobileDevice)
.map(device -> device.replace(":", ""))
.map(String::trim)
.collect(Collectors.toSet())
.stream()
.allMatch(device -> device.equals(""))) {
HashSet<String> mobileDevices = Arrays.stream(mobileDevice())
.map(String::trim).collect(Collectors.toCollection(HashSet::new));

if (mobileDevices.isEmpty() || mobileDevices.stream().allMatch(String::isEmpty)) {
throw new CourgetteException("Mobile device is required when using the Courgette Mobile Device Allocator plugin");
}

if (mobileDeviceType().equals(MobileDeviceType.SIMULATOR)
&& !mobileDevices.stream().allMatch(device -> device.split(":").length == 1)) {
throw new CourgetteException("You must only provide simulator device names (without udid) in the mobileDevice list when using mobile device type: SIMULATOR");
}

if (mobileDeviceType().equals(MobileDeviceType.REAL_DEVICE)
&& !mobileDevices.stream().allMatch(device -> device.split(":").length == 2)) {
throw new CourgetteException("You must only provide real mobile devices (device:udid) in the mobileDevice list when using mobile device type: REAL_DEVICE");
}

boolean noRealDevices = mobileDeviceType().equals(MobileDeviceType.SIMULATOR) || mobileDevices.stream().noneMatch(device -> device.split(":").length == 2);

if (mobileDeviceType().equals(MobileDeviceType.SIMULATOR_AND_REAL_DEVICE) && noRealDevices) {
throw new CourgetteException("You must provide a real mobile device (device:udid) in the mobileDevice list when using mobile device type: SIMULATOR_AND_REAL_DEVICE");
}

if (realMobileDeviceTag().length > 0 && noRealDevices) {
throw new CourgetteException("You must provide a real mobile device (device:udid) in the mobileDevice list when using the realMobileDeviceTag option");
}
}
}

Expand Down
Loading

0 comments on commit 6a38cbe

Please sign in to comment.