Skip to content
Draft
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
263 changes: 179 additions & 84 deletions build.gradle

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions gradle/jacoco-root.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import org.gradle.testing.jacoco.tasks.JacocoReport
import org.gradle.api.tasks.testing.Test

// Apply Jacoco at the root to support aggregate reporting
apply plugin: 'jacoco'

// Define exclusions for JaCoCo coverage
def coverageExclusions = [
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*'
]

// Aggregate Jacoco coverage report for all Android library modules
tasks.register('jacocoRootReport', JacocoReport) {
group = 'verification'
description = 'Generates an aggregate JaCoCo coverage report for all modules'

def fileFilter = coverageExclusions

// Collect class directories from all subprojects
def classDirs = subprojects.collectMany { proj ->
def b = proj.buildDir

// Try multiple possible class directory locations for different AGP versions
// NOTE: If new modules use different compilation output directories, add them here
def possibleClassDirs = [
new File(b, "intermediates/javac/debug/compileDebugJavaWithJavac/classes"), // AGP 9.0+ location (primary)
new File(b, "classes/java/main") // Standard fallback location
]

possibleClassDirs.findAll { it.exists() && it.isDirectory() }.collect { dir ->
proj.fileTree(dir: dir, excludes: fileFilter)
}
}
classDirectories.from = files(classDirs)

// Collect source directories from all subprojects
def srcDirs = subprojects.collectMany { proj ->
[proj.file("src/main/java"), proj.file("src/main/kotlin")]
}.findAll { it.exists() }
sourceDirectories.from = files(srcDirs)

// Collect execution data from all subprojects
def execFiles = subprojects.collectMany { proj ->
def b = proj.buildDir
[
new File(b, "jacoco/testDebugUnitTest.exec"),
new File(b, "outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec")
].findAll { it.exists() }
}
executionData.from = files(execFiles)

doFirst {
logger.lifecycle("=== JaCoCo Root Report Generation ===")
logger.lifecycle("Execution data files:")
def execDataFiles = executionData.files
if (execDataFiles.isEmpty() || !execDataFiles.any { it.exists() }) {
logger.warn(" - No execution data files found - coverage report will be empty")
} else {
execDataFiles.each { file ->
if (file.exists()) {
logger.lifecycle(" - Found: $file (${file.length()} bytes)")
} else {
logger.lifecycle(" - Missing: $file")
}
}
}
logger.lifecycle("=======================================")
}

reports {
xml.required = true
html.required = true
csv.required = false

xml.outputLocation = file("$buildDir/reports/jacoco/jacocoRootReport/jacocoRootReport.xml")
html.outputLocation = file("$buildDir/reports/jacoco/jacocoRootReport/html")
}

// Always regenerate the report
outputs.upToDateWhen { false }
}

// Wire all module unit tests into the aggregate Jacoco report
subprojects { proj ->
// Only consider projects that apply the Jacoco plugin
plugins.withId('jacoco') {
tasks.withType(Test).matching { it.name == 'testDebugUnitTest' }.configureEach { testTask ->
rootProject.tasks.named('jacocoRootReport') {
dependsOn(testTask)
}
}
}
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
6 changes: 0 additions & 6 deletions jacoco.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,6 @@ tasks.register('jacocoTestReport', JacocoReport) {
}
}

// Log class directories
logger.lifecycle("Class directories:")
classDirectories.files.each { dir ->
logger.lifecycle(" - $dir (${dir.exists() ? 'exists' : 'missing'})")
}

// Log execution data files
def execDataFiles = executionData.files
logger.lifecycle("Execution data files:")
Expand Down
4 changes: 4 additions & 0 deletions main/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ configurations.matching { it.name.toLowerCase().contains("test") }.all {
force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0"
}
}

tasks.withType(Test).configureEach {
maxParallelForks = Runtime.getRuntime().availableProcessors().intdiv(2) ?: 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ public void close() {

}

@VisibleForTesting
@Nullable
SSLSocketFactory getSslSocketFactory() {
return mSslSocketFactory;
}

private Proxy initializeProxy(HttpProxy proxy) {
if (proxy != null) {
return new Proxy(
Expand Down Expand Up @@ -279,14 +285,17 @@ public HttpClient build() {

if (mProxy != null) {
mSslSocketFactory = createSslSocketFactoryFromProxy(mProxy);
} else {
} else if (LegacyTlsUpdater.couldBeOld()) {
try {
mSslSocketFactory = new Tls12OnlySocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
Logger.e("TLS v12 algorithm not available: " + e.getLocalizedMessage());
} catch (Exception e) {
Logger.e("Unknown TLS v12 error: " + e.getLocalizedMessage());
}
} else {
// Use platform default
mSslSocketFactory = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

Expand Down Expand Up @@ -403,6 +405,40 @@ public SplitAuthenticatedRequest authenticate(@NonNull SplitAuthenticatedRequest
mProxyServer.shutdown();
}

@Test
public void buildUsesTls12FactoryWhenLegacyAndNoProxy() throws Exception {
Context context = mock(Context.class);

try (MockedStatic<LegacyTlsUpdater> legacyMock = Mockito.mockStatic(LegacyTlsUpdater.class)) {
legacyMock.when(LegacyTlsUpdater::couldBeOld).thenReturn(true);

HttpClient legacyClient = new HttpClientImpl.Builder()
.setContext(context)
.setUrlSanitizer(mUrlSanitizerMock)
.build();

legacyMock.verify(() -> LegacyTlsUpdater.update(context));
assertTrue(((HttpClientImpl) legacyClient).getSslSocketFactory() instanceof Tls12OnlySocketFactory);
}
}

@Test
public void buildUsesDefaultSslWhenNotLegacyAndNoProxy() throws Exception {
Context context = mock(Context.class);

try (MockedStatic<LegacyTlsUpdater> legacyMock = Mockito.mockStatic(LegacyTlsUpdater.class)) {
legacyMock.when(LegacyTlsUpdater::couldBeOld).thenReturn(false);

HttpClient modernClient = new HttpClientImpl.Builder()
.setContext(context)
.setUrlSanitizer(mUrlSanitizerMock)
.build();

legacyMock.verify(() -> LegacyTlsUpdater.update(context), Mockito.never());
assertNull(((HttpClientImpl) modernClient).getSslSocketFactory());
}
}


@Test
public void copyStreamToByteArrayWithSimpleString() {
Expand Down
32 changes: 24 additions & 8 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,36 @@
sonar.projectKey=splitio_android-client
sonar.projectName=android-client

# Path to source directories
sonar.sources=src/main/java
# Path to source directories (multi-module)
# Root project contains modules: main, logger
sonar.sources=main/src/main/java,logger/src/main/java

# Path to compiled classes
sonar.java.binaries=build/intermediates/runtime_library_classes_dir/debug
# Path to compiled classes (multi-module)
# Include binary paths for all modules: main, logger
sonar.java.binaries=\
main/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes,\
logger/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes

# Path to test directories
sonar.tests=src/test/java,src/androidTest/java,src/sharedTest/java
# Path to dependency/libraries jars (multi-module)
sonar.java.libraries=\
main/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar,\
main/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar,\
main/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar,\
main/build/intermediates/compile_and_runtime_r_class_jar/debugUnitTest/generateDebugUnitTestStubRFile/R.jar,\
logger/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar,\
logger/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar,\
logger/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar,\
logger/build/intermediates/compile_and_runtime_r_class_jar/debugUnitTest/generateDebugUnitTestStubRFile/R.jar

# Path to test directories (multi-module)
# Only include test source folders that are guaranteed to exist in all environments
sonar.tests=main/src/test/java,main/src/androidTest/java,main/src/sharedTest/java,logger/src/test/java

# Encoding of the source code
sonar.sourceEncoding=UTF-8

# Include test coverage reports - prioritize combined report
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
# Include aggregate test coverage report from all modules
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml

# Exclusions
sonar.exclusions=**/R.class,**/R$*.class,**/BuildConfig.*,**/Manifest*.*,**/*Test*.*,android/**/*.*
Expand Down
Loading