lambdaisland / kaocha-cloverage

Code coverage analysis for Kaocha
Eclipse Public License 1.0
34 stars 12 forks source link

NullPointerException when run from lein with `:eval-in :leiningen` #17

Open lread opened 2 years ago

lread commented 2 years ago

Symptom

I was upgrading some pretty dated koacha deps in MrAnderson. It was using version 0.0-32 of the kaocha-cloverage plugin.

After upgrading to the current kaocha versions, I encoutered NullPointerExceptions:

$ lein kaocha-coverage      
Error encountered performing task 'run' with profile(s): 'base,system,user,provided,dev,kaocha'
java.lang.NullPointerException
    at kaocha.plugin.cloverage$run_cloverage.invokeStatic(cloverage.clj:134)
    at kaocha.plugin.cloverage$run_cloverage.invoke(cloverage.clj:128)
    at kaocha.plugin.cloverage$cloverage_main_hook.invokeStatic(cloverage.clj:174)
    at kaocha.plugin.cloverage$cloverage_main_hook.invoke(cloverage.clj:143)
    at clojure.lang.AFn.applyToHelper(AFn.java:154)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.core$apply.invokeStatic(core.clj:669)
    at clojure.core$apply.invoke(core.clj:662)
    at kaocha.plugin$run_hook_STAR_$fn__2714.invoke(plugin.clj:91)
    at clojure.lang.PersistentVector.reduce(PersistentVector.java:343)
    at clojure.core$reduce.invokeStatic(core.clj:6885)
    at clojure.core$reduce.invoke(core.clj:6868)
    at kaocha.plugin$run_hook_STAR_.invokeStatic(plugin.clj:89)
    at kaocha.plugin$run_hook_STAR_.doInvoke(plugin.clj:88)
    at clojure.lang.RestFn.invoke(RestFn.java:445)
    at clojure.lang.AFn.applyToHelper(AFn.java:160)
    at clojure.lang.RestFn.applyTo(RestFn.java:132)
    at clojure.core$apply.invokeStatic(core.clj:673)
    at clojure.core$apply.invoke(core.clj:662)
    at kaocha.plugin$run_hook.invokeStatic(plugin.clj:100)
    at kaocha.plugin$run_hook.doInvoke(plugin.clj:99)
    at clojure.lang.RestFn.invoke(RestFn.java:425)
    at kaocha.runner$run$fn__3711.invoke(runner.clj:131)
    at clojure.lang.AFn.applyToHelper(AFn.java:152)
    at clojure.lang.AFn.applyTo(AFn.java:144)
    at clojure.core$apply.invokeStatic(core.clj:667)
    at clojure.core$with_bindings_STAR_.invokeStatic(core.clj:1990)
    at clojure.core$with_bindings_STAR_.doInvoke(core.clj:1990)
    at clojure.lang.RestFn.invoke(RestFn.java:425)
    at kaocha.runner$run.invokeStatic(runner.clj:130)
    at kaocha.runner$run.invoke(runner.clj:73)
    at kaocha.runner$_main_STAR_.invokeStatic(runner.clj:176)
    at kaocha.runner$_main_STAR_.doInvoke(runner.clj:144)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.core$apply.invokeStatic(core.clj:667)
    at clojure.core$apply.invoke(core.clj:662)
    at kaocha.runner$_main.invokeStatic(runner.clj:187)
    at kaocha.runner$_main.doInvoke(runner.clj:185)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:388)
    at user$eval663.invokeStatic(NO_SOURCE_FILE:0)
    at user$eval663.invoke(NO_SOURCE_FILE)
    at clojure.lang.Compiler.eval(Compiler.java:7194)
    at clojure.lang.Compiler.eval(Compiler.java:7184)
    at clojure.lang.Compiler.eval(Compiler.java:7149)
    at clojure.core$eval.invokeStatic(core.clj:3215)
    at clojure.core$eval.invoke(core.clj:3211)
    at leiningen.core.eval$fn__7195.invokeStatic(eval.clj:344)
    at leiningen.core.eval$fn__7195.invoke(eval.clj:336)
    at clojure.lang.MultiFn.invoke(MultiFn.java:234)
    at leiningen.core.eval$eval_in_project.invokeStatic(eval.clj:368)
    at leiningen.core.eval$eval_in_project.invoke(eval.clj:358)
    at leiningen.core.eval$eval_in_project.invokeStatic(eval.clj:362)
    at leiningen.core.eval$eval_in_project.invoke(eval.clj:358)
    at leiningen.run$run_main.invokeStatic(run.clj:130)
    at leiningen.run$run_main.invoke(run.clj:123)
    at leiningen.run$run.invokeStatic(run.clj:157)
    at leiningen.run$run.doInvoke(run.clj:134)
    at clojure.lang.RestFn.applyTo(RestFn.java:139)
    at clojure.lang.Var.applyTo(Var.java:705)
    at clojure.core$apply.invokeStatic(core.clj:669)
    at clojure.core$apply.invoke(core.clj:662)
    at leiningen.core.main$partial_task$fn__7376.doInvoke(main.clj:284)
    at clojure.lang.RestFn.applyTo(RestFn.java:139)
    at clojure.lang.AFunction$1.doInvoke(AFunction.java:31)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.core$apply.invokeStatic(core.clj:669)
    at clojure.core$apply.invoke(core.clj:662)
    at leiningen.core.main$apply_task.invokeStatic(main.clj:334)
    at leiningen.core.main$apply_task.invoke(main.clj:320)
    at leiningen.with_profile$with_profiles_STAR_.invokeStatic(with_profile.clj:14)
    at leiningen.with_profile$with_profiles_STAR_.invoke(with_profile.clj:8)
    at leiningen.with_profile$apply_task_with_profiles.invokeStatic(with_profile.clj:53)
    at leiningen.with_profile$apply_task_with_profiles.invoke(with_profile.clj:45)
    at leiningen.with_profile$with_profile$fn__12011.invoke(with_profile.clj:85)
    at clojure.core$mapv$fn__8535.invoke(core.clj:6979)
    at clojure.core.protocols$fn__8249.invokeStatic(protocols.clj:168)
    at clojure.core.protocols$fn__8249.invoke(protocols.clj:124)
    at clojure.core.protocols$fn__8204$G__8199__8213.invoke(protocols.clj:19)
    at clojure.core.protocols$seq_reduce.invokeStatic(protocols.clj:31)
    at clojure.core.protocols$fn__8236.invokeStatic(protocols.clj:75)
    at clojure.core.protocols$fn__8236.invoke(protocols.clj:75)
    at clojure.core.protocols$fn__8178$G__8173__8191.invoke(protocols.clj:13)
    at clojure.core$reduce.invokeStatic(core.clj:6886)
    at clojure.core$mapv.invokeStatic(core.clj:6970)
    at clojure.core$mapv.invoke(core.clj:6970)
    at leiningen.with_profile$with_profile.invokeStatic(with_profile.clj:85)
    at leiningen.with_profile$with_profile.doInvoke(with_profile.clj:63)
    at clojure.lang.RestFn.applyTo(RestFn.java:146)
    at clojure.lang.Var.applyTo(Var.java:705)
    at clojure.core$apply.invokeStatic(core.clj:669)
    at clojure.core$apply.invoke(core.clj:662)
    at leiningen.core.main$partial_task$fn__7376.doInvoke(main.clj:284)
    at clojure.lang.RestFn.invoke(RestFn.java:410)
    at clojure.lang.AFn.applyToHelper(AFn.java:154)
    at clojure.lang.RestFn.applyTo(RestFn.java:132)
    at clojure.lang.AFunction$1.doInvoke(AFunction.java:31)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.core$apply.invokeStatic(core.clj:669)
    at clojure.core$apply.invoke(core.clj:662)
    at leiningen.core.main$apply_task.invokeStatic(main.clj:334)
    at leiningen.core.main$apply_task.invoke(main.clj:320)
    at leiningen.core.main$resolve_and_apply.invokeStatic(main.clj:343)
    at leiningen.core.main$resolve_and_apply.invoke(main.clj:336)
    at leiningen.core.main$_main$fn__7465.invoke(main.clj:453)
    at leiningen.core.main$_main.invokeStatic(main.clj:442)
    at leiningen.core.main$_main.doInvoke(main.clj:439)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.lang.Var.applyTo(Var.java:705)
    at clojure.core$apply.invokeStatic(core.clj:667)
    at clojure.main$main_opt.invokeStatic(main.clj:514)
    at clojure.main$main_opt.invoke(main.clj:510)
    at clojure.main$main.invokeStatic(main.clj:664)
    at clojure.main$main.doInvoke(main.clj:616)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.lang.Var.applyTo(Var.java:705)
    at clojure.main.main(main.java:40)

Exploration

I had a peek a the plugin code that throws and saw: https://github.com/lambdaisland/kaocha-cloverage/blob/14f63cd298fdfa2fdbb60108472b3a533a376aa1/src/kaocha/plugin/cloverage.clj#L142-L146

If I try this in code in a lein repl I can reproduce the nil:

$ lein repl
nREPL server started on port 54918 on host 127.0.0.1 - nrepl://127.0.0.1:54918
REPL-y 0.5.1, nREPL 0.9.0
Clojure 1.11.1
OpenJDK 64-Bit Server VM 11.0.16.1+1
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (-> [] (.getClass) (.getClassLoader))
nil

If I do this from a clojure REPL all is good:

$ clj
Clojure 1.11.1
user=> (-> [] (.getClass) (.getClassLoader))
#object[jdk.internal.loader.ClassLoaders$AppClassLoader 0x277050dc "jdk.internal.loader.ClassLoaders$AppClassLoader@277050dc"]

I noticed that this change came from lein-cloverage and found a similar issue raised over there.

I then noticed MrAnderson is using :eval-in :leiningen in its project.clj.

Workaround

In MrAnderson's project.clj :kaocha profile, I set :eval-in :sub-process.

Next Steps

Please feel free to close this issue if it is expected behaviour.

If it does represent an issue, I am happy to help in any way I can.

plexus commented 2 years ago

Hey, thanks for reporting this. I'm not sure what the intention is of (-> [] .getClass .getClassLoader), maybe @skuro remembers.

If we simply want to scan for data_reader files then I see no reasing why we can't use the currentContextClassloader. If for some reason we really want the system app classloader, then there's (ClassLoader/getSystemClassLoader) for that.

So yes a PR would be very appreciated. Try replacing (-> [] (.getClass) (.getClassLoader)) with (.getCurrenctContextClassloader (Thread/currentThread)). I see no reason why that wouldn't work. The only potential downside is that depending on which specific combo of tools you are using you could get quite a few different resutls from that call, but pretty much any compliant classloader should be good enough here. A similar change could also be made in lein-cloverage.

lread commented 2 years ago

Thanks for the reply @plexus!

I can't say I understand the change nor why it doesn't work when :eval-in leiningen is active.

But... maybe that does not matter too much. This change was trying to make sure data readers were found and enabled. I could check that this is the case with (.getCurrenctContextClassloader (Thread/currentThread)) regardless of lein :eval-in values.

humorless commented 1 year ago

Hello @lread ,

My lein is of version Leiningen 2.9.8, and I have tried several different java, including Java.net, Microsoft, Temurin, Zulu.

It seems that the issues resolved in newer leiningen version.

humorless commented 1 year ago

It seems that the code

(-> [] (.getClass) (.getClassLoader))

is the same like here.

lread commented 1 year ago

Hi @humorless, thanks for taking a look! :heart:

It seems that the code

(-> [] (.getClass) (.getClassLoader))

is the same like here.

Yes, this is what I noticed too and reported in the original issue. But both @plexus and I did not exactly understand why.

I'm curious, Is this issue closed because it is fixed? Or because it is not considered an issue? Or because it is stale and there wasn't much interest in fixing?

humorless commented 1 year ago

Hi @humorless, thanks for taking a look! ❤️

It seems that the code

(-> [] (.getClass) (.getClassLoader))

is the same like here.

Yes, this is what I noticed too and reported in the original issue. But both @plexus and I did not exactly understand why.

I'm curious, Is this issue closed because it is fixed? Or because it is not considered an issue? Or because it is stale and there wasn't much interest in fixing?

Hello @lread,

I have replied this issue, and I previously thought we get to good enough answer. (It seems that I was wrong.) I reopen it now.

I made my judgement by two facts which I believe. (But, you know, sometimes, I make mistakes.)

  1. (-> [] (.getClass) (.getClassLoader)) is a correct way to get ClassLoader. I have done certain search in the Internet.
  2. I can not reproduce (-> [] (.getClass) (.getClassLoader)) => nil in Leiningen repl.

Therefore, I guessed probably the issue disappears in the newer version of Leiningen.

lread commented 1 year ago

Cool, thanks for following up!

alysbrooks commented 1 year ago

We may actually want to close this issue if we can't reproduce it in the latest version of Leningen. @lread, have you run into this issue recently? Do you happen to know which version of Leiningen you were using?

I suppose another reason to keep this open is the (-> [] (.getClass) (.getClassLoader)) code. Although, based on Laurence's investigation, it's not clear why this wouldn't work. I almost wonder if it's an issue in Leiningen (perhaps fixed)?

JavaDocs for Class.getClassLoader say:

Every Class object contains a reference to the ClassLoader that defined it.

alysbrooks commented 1 year ago

Okay, I did the obvious thing and tried lein repl on my machine. It failed on Leiningen 2.9.1 on Ubuntu on Java 13 with Clojure 1.10.1. However, it succeeded on a project running Clojure 1.10.3 and when I changed it to run Clojure 1.10.2. I thought it was the Clojrue version but then I tried it in the same project with Clojure 1.10.1 and it worked there.

I did find this on Clojuredocs:

`clojure

;; WARNING: If the examples above don't seem to return the expected results, ;; be aware that tools like Leiningen[https://leiningen.org/] or ;; nRepl[https://github.com/nrepl/nrepl] alter the context class loader. ;; Please use a vanilla REPL like ;; clojure.deps[https://clojure.org/guides/getting_started] instead. `

I wonder if nREPL is really to blame. They did fix an issue with the context class loader in 0.6.0. I can't test earlier versions of nREPL since manually setting an older version of nREPL in the project.clj file crashes.