• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Camelcade / Perl5-IDEA / #525521541

22 May 2025 06:26AM UTC coverage: 82.228% (-0.06%) from 82.29%
#525521541

push

github

hurricup
Fail if SDK is null

30870 of 37542 relevant lines covered (82.23%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.9
/plugin/core/src/main/java/com/perl5/lang/perl/adapters/PackageManagerAdapter.java
1
/*
2
 * Copyright 2015-2025 Alexandr Evstigneev
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.perl5.lang.perl.adapters;
18

19
import com.intellij.execution.process.ProcessAdapter;
20
import com.intellij.execution.process.ProcessEvent;
21
import com.intellij.openapi.actionSystem.AnAction;
22
import com.intellij.openapi.actionSystem.AnActionEvent;
23
import com.intellij.openapi.application.ApplicationManager;
24
import com.intellij.openapi.diagnostic.Logger;
25
import com.intellij.openapi.project.Project;
26
import com.intellij.openapi.projectRoots.Sdk;
27
import com.intellij.openapi.util.AtomicNotNullLazyValue;
28
import com.intellij.openapi.util.NlsSafe;
29
import com.intellij.openapi.util.Pair;
30
import com.intellij.openapi.util.text.StringUtil;
31
import com.intellij.openapi.vfs.VirtualFile;
32
import com.intellij.util.ui.update.MergingUpdateQueue;
33
import com.intellij.util.ui.update.Update;
34
import com.perl5.PerlBundle;
35
import com.perl5.lang.perl.idea.actions.PerlDumbAwareAction;
36
import com.perl5.lang.perl.idea.execution.PerlCommandLine;
37
import com.perl5.lang.perl.idea.project.PerlProjectManager;
38
import com.perl5.lang.perl.idea.sdk.host.PerlHostData;
39
import com.perl5.lang.perl.util.PerlPluginUtil;
40
import com.perl5.lang.perl.util.PerlRunUtil;
41
import org.jetbrains.annotations.NotNull;
42
import org.jetbrains.annotations.Nullable;
43
import org.jetbrains.annotations.TestOnly;
44
import org.jetbrains.annotations.VisibleForTesting;
45

46
import java.util.*;
47
import java.util.concurrent.TimeUnit;
48
import java.util.concurrent.TimeoutException;
49

50
public abstract class PackageManagerAdapter {
51
  public static final @NlsSafe String CPANMINUS_PACKAGE_NAME = "App::cpanminus";
52

53
  private static final Logger LOG = Logger.getInstance(PackageManagerAdapter.class);
1✔
54
  private static final AtomicNotNullLazyValue<MergingUpdateQueue> QUEUE_PROVIDER = AtomicNotNullLazyValue.createValue(() ->
1✔
55
                                                                                                                        new MergingUpdateQueue(
1✔
56
                                                                                                                          "perl.installer.queue",
57
                                                                                                                          300, true, null,
58
                                                                                                                          PerlPluginUtil.getUnloadAwareDisposable()));
1✔
59

60
  private final @NotNull Sdk mySdk;
61

62
  private final @Nullable Project myProject;
63

64
  public PackageManagerAdapter(@NotNull Sdk sdk, @Nullable Project project) {
1✔
65
    mySdk = sdk;
1✔
66
    myProject = project;
1✔
67
  }
1✔
68

69
  public abstract @NotNull String getPresentableName();
70

71
  protected abstract @NotNull String getManagerScriptName();
72

73
  protected abstract @NotNull String getManagerPackageName();
74

75
  /**
76
   * @return list of parameters for manager's script to install a package with {@code packageName}
77
   */
78
  protected @NotNull List<String> getInstallParameters(@NotNull Collection<String> packageNames, boolean suppressTests) {
79
    return new ArrayList<>(packageNames);
1✔
80
  }
81

82
  /**
83
   * This method installs packages with given package names using manager's script.
84
   *
85
   * @param packageNames  a collection of strings representing package names to install
86
   * @param callback      a runnable object to execute when installation is complete
87
   * @param suppressTests true iff tests should be suppressed during installation
88
   * @apiNote this method starts installation process immediately. If you need a deferred installation (with 300ms) and it is possible that
89
   * more packages may come for installation, use {@link #queueInstall(Collection, boolean)}
90
   */
91
  public void install(@NotNull Collection<String> packageNames, @NotNull Runnable callback, boolean suppressTests) {
92
    doInstall(packageNames, callback, suppressTests);
×
93
  }
×
94

95
  /**
96
   * Asynchronously installs {@code packageNames} with output in the console
97
   *
98
   * @param callback      optional callback to be invoked after installing and paths.
99
   * @param suppressTests tue iff tests should not run during installation
100
   */
101
  private void doInstall(@NotNull Collection<String> packageNames, @Nullable Runnable callback, boolean suppressTests) {
102
    ApplicationManager.getApplication().assertIsDispatchThread();
1✔
103
    if (myProject != null && myProject.isDisposed()) {
1✔
104
      return;
×
105
    }
106
    VirtualFile script = PerlRunUtil.findLibraryScriptWithNotification(
1✔
107
      getSdk(), getProject(), getManagerScriptName(), getManagerPackageName());
1✔
108
    if (script == null) {
1✔
109
      LOG.warn("Unable to find a script: " +
×
110
               "sdk=" + getSdk() +
×
111
               "; project=" + getProject() +
×
112
               "; scriptName=" + getManagerScriptName() +
×
113
               "; packageName=" + getManagerPackageName());
×
114
      return;
×
115
    }
116
    String remotePath = PerlHostData.notNullFrom(mySdk).getRemotePath(script.getPath());
1✔
117
    PerlRunUtil.runInConsole(
1✔
118
      new PerlCommandLine(remotePath)
119
        .withParameters(getInstallParameters(packageNames, suppressTests))
1✔
120
        .withSdk(getSdk())
1✔
121
        .withProject(getProject())
1✔
122
        .withConsoleTitle(PerlBundle.message("perl.cpan.console.installing", StringUtil.join(packageNames, ", ")))
1✔
123
        .withProcessListener(new ProcessAdapter() {
1✔
124
          @Override
125
          public void processTerminated(@NotNull ProcessEvent event) {
126
            PerlRunUtil.refreshSdkDirs(mySdk, myProject, () -> {
1✔
127
              if (event.getExitCode() == 0 && callback != null) {
1✔
128
                callback.run();
×
129
              }
130
            });
1✔
131
          }
1✔
132
        })
133
    );
134
  }
1✔
135

136
  /**
137
   * Queues {@code packageNames} for installation by adding them to the update queue for asynchronous processing.
138
   * <p>
139
   * Note that this method only adds the packages to the queue for installation; it does not perform the installation itself.
140
   * Use {@link #install(Collection, Runnable, boolean)} to actually install the packages.
141
   *
142
   * @param packageNames  the collection of package names to be installed
143
   * @param suppressTests {@code true} if tests should not run during installation; {@code false} otherwise
144
   * @see #install(Collection, Runnable, boolean)
145
   */
146
  public final void queueInstall(@NotNull Collection<String> packageNames, boolean suppressTests) {
147
    QUEUE_PROVIDER.get().queue(new InstallUpdate(this, packageNames, suppressTests));
1✔
148
  }
1✔
149

150
  /**
151
   * Queues the given {@code packageName} for installation by adding it to the update queue for asynchronous processing.
152
   * <p>
153
   * Note that this method only adds the package to the queue for installation; it does not perform the installation itself.
154
   * Use {@link #install(Collection, Runnable, boolean)} to actually install the package.
155
   *
156
   * @param packageName the name of the package to be installed
157
   */
158
  public final void queueInstall(@NotNull String packageName) {
159
    queueInstall(Collections.singletonList(packageName), false);
×
160
  }
×
161

162
  @Override
163
  public boolean equals(Object o) {
164
    if (this == o) {
×
165
      return true;
×
166
    }
167
    if (o == null || getClass() != o.getClass()) {
×
168
      return false;
×
169
    }
170

171
    PackageManagerAdapter adapter = (PackageManagerAdapter)o;
×
172

173
    if (!mySdk.equals(adapter.mySdk)) {
×
174
      return false;
×
175
    }
176
    return myProject != null ? myProject.equals(adapter.myProject) : adapter.myProject == null;
×
177
  }
178

179
  @Override
180
  public int hashCode() {
181
    int result = mySdk.hashCode();
1✔
182
    result = 31 * result + (myProject != null ? myProject.hashCode() : 0);
1✔
183
    return result;
1✔
184
  }
185

186
  public @NotNull Sdk getSdk() {
187
    return mySdk;
1✔
188
  }
189

190
  private @Nullable Project getProject() {
191
    return myProject;
1✔
192
  }
193

194
  /**
195
   * Installs {@code libraryNames} into {@code sdk} and invoking a {@code callback} if any
196
   */
197
  public static void installModules(@NotNull Sdk sdk,
198
                                    @Nullable Project project,
199
                                    @NotNull Collection<String> libraryNames,
200
                                    @Nullable Runnable actionCallback,
201
                                    boolean suppressTests) {
202
    installModules(PackageManagerAdapterFactory.create(sdk, project), libraryNames, actionCallback, suppressTests);
×
203
  }
×
204

205
  private static void installModules(@NotNull PackageManagerAdapter adapter,
206
                                     @NotNull Collection<String> libraryNames,
207
                                     @Nullable Runnable actionCallback,
208
                                     boolean suppressTests) {
209
    adapter.queueInstall(libraryNames, suppressTests);
1✔
210
    if (actionCallback != null) {
1✔
211
      actionCallback.run();
1✔
212
    }
213
  }
1✔
214

215
  public static @NotNull AnAction createInstallAction(@NotNull Sdk sdk,
216
                                                      @Nullable Project project,
217
                                                      @NotNull Collection<String> libraryNames,
218
                                                      @Nullable Runnable actionCallback) {
219
    return createInstallAction(PackageManagerAdapterFactory.create(sdk, project), libraryNames, actionCallback);
1✔
220
  }
221

222
  @VisibleForTesting
223
  public static @NotNull PerlDumbAwareAction createInstallAction(@NotNull PackageManagerAdapter adapter,
224
                                                                 @NotNull Collection<String> libraryNames,
225
                                                                 @Nullable Runnable actionCallback) {
226
    @NotNull String toolName = adapter.getManagerScriptName();
1✔
227
    return new PerlDumbAwareAction(PerlBundle.message("perl.quickfix.install.family", toolName)) {
1✔
228
      @Override
229
      public void actionPerformed(@NotNull AnActionEvent e) {
230
        installModules(adapter, libraryNames, actionCallback, true);
1✔
231
      }
1✔
232
    };
233
  }
234

235
  public static void installCpanminus(@Nullable Project project) {
236
    @Nullable Sdk sdk = PerlProjectManager.getSdk(project);
×
237
    if (sdk == null) {
×
238
      LOG.debug("No sdk for installation");
×
239
      return;
×
240
    }
241
    PackageManagerAdapterFactory.create(sdk, project).queueInstall(CPANMINUS_PACKAGE_NAME);
×
242
  }
×
243

244
  private static final class InstallUpdate extends Update {
245
    private final @NotNull PackageManagerAdapter myAdapter;
246
    private final boolean mySuppressTests;
247
    private final @NotNull Set<String> myPackages = new LinkedHashSet<>();
1✔
248

249
    public InstallUpdate(@NotNull PackageManagerAdapter adapter, @NotNull Collection<String> packageNames, boolean suppressTests) {
250
      super(Pair.create(adapter, packageNames));
1✔
251
      myAdapter = adapter;
1✔
252
      myPackages.addAll(packageNames);
1✔
253
      mySuppressTests = suppressTests;
1✔
254
    }
1✔
255

256
    @Override
257
    public void run() {
258
      myAdapter.doInstall(myPackages, null, mySuppressTests);
1✔
259
    }
1✔
260

261
    @Override
262
    public boolean canEat(Update update) {
263
      if (!(update instanceof InstallUpdate installUpdate)) {
×
264
        return super.canEat(update);
×
265
      }
266
      myPackages.addAll(installUpdate.myPackages);
×
267
      return true;
×
268
    }
269
  }
270

271
  @TestOnly
272
  public static void waitForAllExecuted() throws TimeoutException {
273
    QUEUE_PROVIDER.getValue().waitForAllExecuted(2, TimeUnit.MINUTES);
1✔
274
  }
1✔
275
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc