-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug(#368): unlint-non-existing-defect
in WPA scope
#369
Changes from all commits
da61115
9e5f4dc
1f57755
f090b5f
5720bc3
f7e94a0
662c64a
f1b7577
7b7bd72
dadaaa1
15e564e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,149 @@ | ||||
/* | ||||
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com | ||||
* SPDX-License-Identifier: MIT | ||||
*/ | ||||
package org.eolang.lints; | ||||
|
||||
import com.github.lombrozo.xnav.Xnav; | ||||
import com.jcabi.xml.XML; | ||||
import java.io.IOException; | ||||
import java.util.Collection; | ||||
import java.util.HashMap; | ||||
import java.util.LinkedList; | ||||
import java.util.List; | ||||
import java.util.Map; | ||||
import java.util.stream.Collectors; | ||||
import org.cactoos.io.ResourceOf; | ||||
import org.cactoos.list.ListOf; | ||||
import org.cactoos.text.IoCheckedText; | ||||
import org.cactoos.text.TextOf; | ||||
|
||||
/** | ||||
* Lint for checking `+unlint` meta to suppress non-existing defects in WPA scope. | ||||
* This lint was not included in {@link LtUnlintNonExistingDefect}, because we need | ||||
* to aggregate the XMIR defects using supplied lints. In {@link LtUnlintNonExistingDefect} | ||||
* we work with single program scope, while this class works with WPA scope. | ||||
* | ||||
* @see LtUnlintNonExistingDefect | ||||
* @since 0.0.42 | ||||
*/ | ||||
final class LtUnlintNonExistingDefectWpa implements Lint<Map<String, XML>> { | ||||
|
||||
/** | ||||
* Lints. | ||||
*/ | ||||
private final Iterable<Lint<Map<String, XML>>> lints; | ||||
|
||||
/** | ||||
* Ctor. | ||||
* | ||||
* @param lnts Lints | ||||
*/ | ||||
LtUnlintNonExistingDefectWpa(final Iterable<Lint<Map<String, XML>>> lnts) { | ||||
this.lints = lnts; | ||||
} | ||||
|
||||
@Override | ||||
public String name() { | ||||
return "unlint-non-existing-defect"; | ||||
} | ||||
|
||||
@Override | ||||
public Collection<Defect> defects(final Map<String, XML> pkg) { | ||||
final Collection<Defect> defects = new LinkedList<>(); | ||||
final Map<XML, List<String>> existing = this.existingDefects(pkg); | ||||
pkg.values().forEach( | ||||
xmir -> { | ||||
final Xnav xml = new Xnav(xmir.inner()); | ||||
final List<String> present = existing.get(xmir); | ||||
xml.path("/program/metas/meta[head='unlint']/tail") | ||||
.map(xnav -> xnav.text().get()) | ||||
.collect(Collectors.toSet()) | ||||
.stream() | ||||
.filter(unlint -> !present.contains(unlint)) | ||||
.forEach( | ||||
unlint -> xml | ||||
.path( | ||||
String.format( | ||||
"program/metas/meta[head='unlint' and tail='%s']/@line", unlint | ||||
) | ||||
) | ||||
.map(xnav -> xnav.text().get()) | ||||
.collect(Collectors.toList()) | ||||
.forEach( | ||||
line -> defects.add( | ||||
new Defect.Default( | ||||
this.name(), | ||||
Severity.WARNING, | ||||
xml.element("program") | ||||
.attribute("name") | ||||
.text() | ||||
.orElse("unknown"), | ||||
Integer.parseInt(line), | ||||
String.format( | ||||
"Unlinting rule '%s' doesn't make sense, since there are no defects with it", | ||||
unlint | ||||
) | ||||
) | ||||
) | ||||
) | ||||
); | ||||
} | ||||
); | ||||
return defects; | ||||
} | ||||
|
||||
@Override | ||||
public String motive() throws IOException { | ||||
return new IoCheckedText( | ||||
new TextOf( | ||||
new ResourceOf( | ||||
String.format( | ||||
"org/eolang/motives/misc/%s.md", this.name() | ||||
) | ||||
) | ||||
) | ||||
).asString(); | ||||
} | ||||
|
||||
/** | ||||
* Find existing defects. | ||||
* | ||||
* @param pkg Package with programs to scan | ||||
* @return Map of existing defects | ||||
*/ | ||||
private Map<XML, List<String>> existingDefects(final Map<String, XML> pkg) { | ||||
final Map<XML, List<String>> aggregated = new HashMap<>(0); | ||||
this.lints.forEach( | ||||
wpl -> { | ||||
try { | ||||
final Collection<Defect> defects = wpl.defects(pkg); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @h1alexbel can this line be move out of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @maxonfjvipon
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @h1alexbel it also seems that during the entire project linting we call |
||||
pkg.values().forEach( | ||||
program -> | ||||
aggregated.merge( | ||||
program, | ||||
new ListOf<>( | ||||
defects.stream() | ||||
.map(Defect::rule) | ||||
.collect(Collectors.toList()) | ||||
), | ||||
(existing, incoming) -> { | ||||
existing.addAll(incoming); | ||||
return existing; | ||||
} | ||||
) | ||||
); | ||||
} catch (final IOException exception) { | ||||
throw new IllegalStateException( | ||||
String.format( | ||||
"IO operation failed while linting package of programs with %s", | ||||
wpl.name() | ||||
), | ||||
exception | ||||
); | ||||
} | ||||
} | ||||
); | ||||
return aggregated; | ||||
} | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
package org.eolang.lints; | ||
|
||
import com.jcabi.xml.XML; | ||
import com.jcabi.xml.XMLDocument; | ||
import java.io.IOException; | ||
import org.cactoos.list.ListOf; | ||
import org.cactoos.map.MapEntry; | ||
import org.cactoos.map.MapOf; | ||
import org.eolang.parser.EoSyntax; | ||
import org.hamcrest.MatcherAssert; | ||
import org.hamcrest.Matchers; | ||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
* Tests for {@link LtUnlintNonExistingDefectWpa}. | ||
* | ||
* @since 0.0.42 | ||
*/ | ||
final class LtUnlintNonExistingDefectWpaTest { | ||
|
||
@Test | ||
void allowsUnlintingExistingWpaDefects() throws IOException { | ||
MatcherAssert.assertThat( | ||
"Lint should not complain, since program has WPA defects", | ||
new LtUnlintNonExistingDefectWpa( | ||
new ListOf<>(new LtUnitTestMissing()) | ||
).defects( | ||
new MapOf<>( | ||
"foo", | ||
new EoSyntax( | ||
String.join( | ||
"\n", | ||
"+unlint unit-test-missing", | ||
"", | ||
"# Foo", | ||
"[] > foo" | ||
) | ||
).parsed() | ||
) | ||
), | ||
Matchers.emptyIterable() | ||
); | ||
} | ||
|
||
@Test | ||
void catchesUnlintOfNonExistingWpaDefects() throws IOException { | ||
MatcherAssert.assertThat( | ||
"Defects are not empty, but they should be, since +unlint unlints non-existing defect", | ||
new LtUnlintNonExistingDefectWpa( | ||
new ListOf<>(new LtUnitTestMissing()) | ||
).defects( | ||
new MapOf<String, XML>( | ||
new MapEntry<>( | ||
"bar", | ||
new EoSyntax( | ||
String.join( | ||
"\n", | ||
"+unlint unit-test-missing", | ||
"", | ||
"# Bar", | ||
"[] > bar" | ||
) | ||
).parsed() | ||
), | ||
new MapEntry<>("bar-tests", new XMLDocument("<program/>")) | ||
) | ||
), | ||
Matchers.hasSize(Matchers.greaterThan(0)) | ||
); | ||
} | ||
|
||
@Test | ||
void allowsNoUnlints() { | ||
MatcherAssert.assertThat( | ||
"Defects are not empty, but they should", | ||
new LtUnlintNonExistingDefectWpa( | ||
new ListOf<>(new LtUnitTestMissing()) | ||
).defects( | ||
new MapOf<String, XML>( | ||
new MapEntry<>("f", new XMLDocument("<program/>")), | ||
new MapEntry<>("f-tests", new XMLDocument("<program/>")) | ||
) | ||
), | ||
Matchers.emptyIterable() | ||
); | ||
} | ||
|
||
@Test | ||
void reportsWithWpaSupplied() throws IOException { | ||
MatcherAssert.assertThat( | ||
"Defects are empty, but they should not", | ||
new LtUnlintNonExistingDefectWpa( | ||
new PkWpa() | ||
).defects( | ||
new MapOf<String, XML>( | ||
new MapEntry<>( | ||
"e-tests", | ||
new EoSyntax( | ||
"e-tests", | ||
String.join( | ||
"\n", | ||
"+unlint unit-test-without-live-file", | ||
"", | ||
"# E tests.", | ||
"[] > runs-e" | ||
) | ||
).parsed() | ||
), | ||
new MapEntry<>("e", new XMLDocument("<program/>")) | ||
) | ||
), | ||
Matchers.hasSize(Matchers.greaterThan(0)) | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@h1alexbel something's still wrong with indentations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxonfjvipon otherwise qulice complains about cascade indentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@h1alexbel how about this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxonfjvipon only this one is valid (dedent with
xml.path()
args):There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@h1alexbel this part looks ugly:
We definitely should find another way. How about this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxonfjvipon the best we can do is this, I believe (since we cannot inline
xml.path
due to too wide line):WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@h1alexbel what about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxonfjvipon I think we did it. Big thanks!