Skip to content

Commit 3e4fd8b

Browse files
Optimize YumPackageInstaller: Use 'rpm -q' instead of 'yum list installed'
This change optimizes the package existence check in `YumPackageInstaller` by replacing the expensive `yum list installed` command with the much faster `rpm -q <package>`. `yum list installed` iterates over all installed packages, which is an O(N) operation and incurs significant overhead. `rpm -q` performs a direct lookup in the RPM database (O(1) / O(log N)). Changes: - Added `executeCommand` to `PackageInstaller` to allow executing commands without enforcing exit code 0 (needed for `rpm -q` on missing packages). - Updated `YumPackageInstaller.shouldInstall` to use `rpm -q`. - Added `YumPackageInstallerTest` to verify the logic. Performance Impact: - Theoretical complexity reduction from O(N) to O(1). - Avoids parsing large output from `yum list installed`. - Improves correctness by avoiding partial string matching issues (e.g. finding "foo-bar" when checking for "foo"). Co-authored-by: Periecle <26135126+Periecle@users.noreply.github.com>
1 parent eb68cd1 commit 3e4fd8b

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

testcontainers-common/src/main/java/com/playtika/testcontainer/common/utils/PackageInstaller.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,8 @@ protected Container.ExecResult executeCommandAndCheckExitCode(String... command)
6666
return ContainerUtils.executeAndCheckExitCode(container, command);
6767
}
6868

69+
protected Container.ExecResult executeCommand(String... command) {
70+
return ContainerUtils.executeInContainer(container, command);
71+
}
72+
6973
}

testcontainers-common/src/main/java/com/playtika/testcontainer/common/utils/YumPackageInstaller.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ public YumPackageInstaller(InstallPackageProperties properties, GenericContainer
1212

1313
@Override
1414
protected boolean shouldInstall(String packageToInstall) {
15-
Container.ExecResult execResult = executeCommandAndCheckExitCode("yum", "list", "installed");
16-
return !execResult.getStdout().contains(packageToInstall);
15+
Container.ExecResult execResult = executeCommand("rpm", "-q", packageToInstall);
16+
return execResult.getExitCode() != 0;
1717
}
1818

1919
@Override
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.playtika.testcontainer.common.utils;
2+
3+
import com.playtika.testcontainer.common.properties.InstallPackageProperties;
4+
import org.junit.jupiter.api.Test;
5+
import org.testcontainers.containers.Container;
6+
import org.testcontainers.containers.GenericContainer;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.mockito.Mockito.mock;
10+
import static org.mockito.Mockito.when;
11+
12+
public class YumPackageInstallerTest {
13+
14+
@Test
15+
public void shouldInstallReturnsTrueWhenPackageMissing() {
16+
InstallPackageProperties properties = new InstallPackageProperties();
17+
GenericContainer<?> container = mock(GenericContainer.class);
18+
19+
TestableYumPackageInstaller installer = new TestableYumPackageInstaller(properties, container);
20+
installer.setMockExitCode(1); // Package missing
21+
22+
assertThat(installer.shouldInstall("missing-package")).isTrue();
23+
assertThat(installer.lastCommand).containsExactly("rpm", "-q", "missing-package");
24+
}
25+
26+
@Test
27+
public void shouldInstallReturnsFalseWhenPackagePresent() {
28+
InstallPackageProperties properties = new InstallPackageProperties();
29+
GenericContainer<?> container = mock(GenericContainer.class);
30+
31+
TestableYumPackageInstaller installer = new TestableYumPackageInstaller(properties, container);
32+
installer.setMockExitCode(0); // Package present
33+
34+
assertThat(installer.shouldInstall("present-package")).isFalse();
35+
assertThat(installer.lastCommand).containsExactly("rpm", "-q", "present-package");
36+
}
37+
38+
static class TestableYumPackageInstaller extends YumPackageInstaller {
39+
private int mockExitCode;
40+
public String[] lastCommand;
41+
42+
public TestableYumPackageInstaller(InstallPackageProperties properties, GenericContainer<?> container) {
43+
super(properties, container);
44+
}
45+
46+
public void setMockExitCode(int code) {
47+
this.mockExitCode = code;
48+
}
49+
50+
@Override
51+
public boolean shouldInstall(String packageToInstall) {
52+
return super.shouldInstall(packageToInstall);
53+
}
54+
55+
@Override
56+
protected Container.ExecResult executeCommand(String... command) {
57+
this.lastCommand = command;
58+
Container.ExecResult result = mock(Container.ExecResult.class);
59+
when(result.getExitCode()).thenReturn(mockExitCode);
60+
when(result.getStdout()).thenReturn("mock output");
61+
return result;
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)