ARTEMIS-5110: add tests for ID validations and suggestions done by log annotation processor

Also improve an error message to report the LogBundle class like others do.
This commit is contained in:
Robbie Gemmell 2024-11-13 16:38:07 +00:00 committed by Timothy Bish
parent bc077f4458
commit e1fad965e7
11 changed files with 343 additions and 11 deletions

View File

@ -154,7 +154,7 @@ public class LogAnnotationProcessor extends AbstractProcessor {
int generatedPaths = 0;
if (messageAnnotation != null) {
validateRegexID(bundleAnnotation, messageAnnotation.id());
validateRegexID(bundleAnnotation.regexID(), executableMember, messageAnnotation.id());
generatedPaths++;
if (DEBUG) {
debug("... annotated with " + messageAnnotation);
@ -163,7 +163,7 @@ public class LogAnnotationProcessor extends AbstractProcessor {
}
if (logAnnotation != null) {
validateRegexID(bundleAnnotation, logAnnotation.id());
validateRegexID(bundleAnnotation.regexID(), executableMember, logAnnotation.id());
generatedPaths++;
if (DEBUG) {
debug("... annotated with " + logAnnotation);
@ -205,17 +205,19 @@ public class LogAnnotationProcessor extends AbstractProcessor {
return true;
}
private static void validateRegexID(LogBundle bundleAnnotation, long id) {
if (!isAllowedIDValue(bundleAnnotation, id)) {
throw new IllegalArgumentException("Code " + id + " does not match regular expression \"" + bundleAnnotation.regexID() + "\" specified on the LogBundle");
private static void validateRegexID(String regexID, ExecutableElement executableMember, long id) {
if (!isAllowedIDValue(regexID, id)) {
String enclosingClass = executableMember.getEnclosingElement().toString();
throw new IllegalArgumentException(enclosingClass + ": Code " + id + " does not match regular expression specified on the LogBundle: " + regexID);
}
}
private static boolean isAllowedIDValue(LogBundle bundleAnnotation, long id) {
if (bundleAnnotation.regexID() != null && !bundleAnnotation.regexID().isEmpty()) {
private static boolean isAllowedIDValue(String regexID, long id) {
if (regexID != null && !regexID.isEmpty()) {
String toStringID = Long.toString(id);
return toStringID.matches(bundleAnnotation.regexID());
return toStringID.matches(regexID);
}
return true;
@ -535,7 +537,7 @@ public class LogAnnotationProcessor extends AbstractProcessor {
nextId++;
}
if (isAllowedIDValue(bundleAnnotation, nextId)) {
if (isAllowedIDValue(bundleAnnotation.regexID(), nextId)) {
failure.append("Consider trying ID ").append(nextId).append(" which is the next unused value.");
} else {
failure.append("There are no new IDs available within the given ID regex: " + bundleAnnotation.regexID());
@ -575,9 +577,10 @@ public class LogAnnotationProcessor extends AbstractProcessor {
return;
}
String regexID = bundleAnnotation.regexID();
for (int id : retiredIDs) {
if (!isAllowedIDValue(bundleAnnotation, id)) {
throw new IllegalArgumentException(annotatedType + ": The retiredIDs elements must each match the configured regexID. The ID " + id + " does not match: " + bundleAnnotation.regexID());
if (!isAllowedIDValue(regexID, id)) {
throw new IllegalArgumentException(annotatedType + ": The retiredIDs elements must each match the configured regexID. The ID " + id + " does not match: " + regexID);
}
}

View File

@ -70,5 +70,12 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- Used to exercise the annotation processor directly -->
<dependency>
<groupId>com.karuslabs</groupId>
<artifactId>elementary</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,110 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.lang.invoke.MethodHandles;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.karuslabs.elementary.Finder;
import com.karuslabs.elementary.Results;
import com.karuslabs.elementary.junit.JavacExtension;
import com.karuslabs.elementary.junit.annotations.Options;
import com.karuslabs.elementary.junit.annotations.Processors;
import com.karuslabs.elementary.junit.annotations.Resource;
@ExtendWith(JavacExtension.class)
@Options("-Werror")
@Processors(LogAnnotationProcessor.class)
public class LogAnnotationProcessorTest {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Test
@Resource("org/apache/activemq/artemis/logs/annotation/processor/cases/LAPTCase1_InvalidIDForRegex.java")
public void testIDInvalidForGivenRegex(Results results) {
final String expectedMessage = "org.apache.activemq.artemis.logs.annotation.processor.cases.LAPTCase1_InvalidIDForRegex: "
+ "Code 100 does not match regular expression specified on the LogBundle: [0-9]{1}";
doCheckFailureErrorMessageTestImpl(results, expectedMessage);
}
@Test
@Resource("org/apache/activemq/artemis/logs/annotation/processor/cases/LAPTCase2_InvalidIDReused.java")
public void testIDInvalidReused(Results results) {
final String expectedMessage = "org.apache.activemq.artemis.logs.annotation.processor.cases.LAPTCase2_InvalidIDReused: "
+ "ID 3 with message 'reusedID' was previously used already, to define message 'initialIDuse'. "
+ "Consider trying ID 5 which is the next unused value.";
doCheckFailureErrorMessageTestImpl(results, expectedMessage);
}
@Test
@Resource("org/apache/activemq/artemis/logs/annotation/processor/cases/LAPTCase3_InvalidIDRetired.java")
public void testIDInvalidRetired(Results results) {
final String expectedMessage = "org.apache.activemq.artemis.logs.annotation.processor.cases.LAPTCase3_InvalidIDRetired: "
+ "ID 2 was previously retired, another ID must be used. "
+ "Consider trying ID 7 which is the next unused value.";
doCheckFailureErrorMessageTestImpl(results, expectedMessage);
}
@Test
@Resource("org/apache/activemq/artemis/logs/annotation/processor/cases/LAPTCase4_InvalidIDRetiredWithGap.java")
public void testIDInvalidRetiredWithGap(Results results) {
final String expectedMessage = "org.apache.activemq.artemis.logs.annotation.processor.cases.LAPTCase4_InvalidIDRetiredWithGap: "
+ "ID 2 was previously retired, another ID must be used. "
+ "Consider trying ID 4 which is the next unused value.";
doCheckFailureErrorMessageTestImpl(results, expectedMessage);
}
@Test
@Resource("org/apache/activemq/artemis/logs/annotation/processor/cases/LAPTCase5_InvalidRetiredID.java")
public void testInvalidRetiredID(Results results) {
final String expectedMessage = "org.apache.activemq.artemis.logs.annotation.processor.cases.LAPTCase5_InvalidRetiredID: "
+ "The retiredIDs elements must each match the configured regexID. "
+ "The ID 10 does not match: [0-9]{1}";
doCheckFailureErrorMessageTestImpl(results, expectedMessage);
}
@Test
@Resource("org/apache/activemq/artemis/logs/annotation/processor/cases/LAPTCase6_UnsortedRetiredID.java")
public void testUnsortedRetiredIDs(Results results) {
final String expectedMessage = "org.apache.activemq.artemis.logs.annotation.processor.cases.LAPTCase6_UnsortedRetiredID: "
+ "The retiredIDs value must be sorted. Try using: {2, 4, 5}";
doCheckFailureErrorMessageTestImpl(results, expectedMessage);
}
private void doCheckFailureErrorMessageTestImpl(Results results, String expectedMessage) {
Finder errors = results.find().errors();
List<String> errorMessages = errors.messages();
logger.trace("Error result messages: {}", errorMessages);
assertEquals(1, errors.count(), () -> "Expected 1 error result. Got : " + errorMessages);
assertEquals(expectedMessage, errorMessages.get(0), "Did not get expected error message.");
}
}

View File

@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor.cases;
import org.apache.activemq.artemis.logs.annotation.LogBundle;
import org.apache.activemq.artemis.logs.annotation.Message;
// Test class used by LogAnnotationProcessorTest to check failure modes.
@LogBundle(projectCode = "LAPTCase1", regexID="[0-9]{1}")
public interface LAPTCase1_InvalidIDForRegex {
// Regex allows IDs 0-9, use a higher value that fails checks.
@Message(id = 100, value = "InvalidIDForRegex")
String invalidIDForRegex();
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor.cases;
import org.apache.activemq.artemis.logs.annotation.LogBundle;
import org.apache.activemq.artemis.logs.annotation.Message;
// Test class used by LogAnnotationProcessorTest to check failure modes.
@LogBundle(projectCode = "LAPTCase2", regexID="[0-9]{1}", retiredIDs = { 1, 2, 4 })
public interface LAPTCase2_InvalidIDReused {
@Message(id = 3, value = "initialIDuse")
String initialIDuse();
// Used ID 3 again, not allowed. Error will suggest ID 5 (rather than 4, since it is also retired).
@Message(id = 3, value = "reusedID")
String reusedID();
}

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor.cases;
import org.apache.activemq.artemis.logs.annotation.LogBundle;
import org.apache.activemq.artemis.logs.annotation.Message;
// Test class used by LogAnnotationProcessorTest to check failure modes.
@LogBundle(projectCode = "LAPTCase3", regexID="[0-9]{1}", retiredIDs = { 2, 5, 6 })
public interface LAPTCase3_InvalidIDRetired {
@Message(id = 1, value = "ignoreMe")
String ignoreMe();
// Used ID 2, not allowed due to being marked retired. Error will suggest ID 7
// (4 is highest 'active' ID, and 5 and 6 are also retired).
@Message(id = 2, value = "retiredID")
String retiredID();
@Message(id = 4, value = "ignoreMeAlso")
String ignoreMeAlso();
}

View File

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor.cases;
import org.apache.activemq.artemis.logs.annotation.LogBundle;
import org.apache.activemq.artemis.logs.annotation.Message;
// Test class used by LogAnnotationProcessorTest to check failure modes.
@LogBundle(projectCode = "LAPTCase4", regexID="[0-9]{1}", retiredIDs = { 2, 9 })
public interface LAPTCase4_InvalidIDRetiredWithGap {
@Message(id = 1, value = "ignoreMe")
String ignoreMe();
// Used ID 2, not allowed due to being marked retired. Error will suggest ID 4
// (since although 9 is retired there is an 'active' gap, 4 is next highest free 'active' not-retired ID)).
@Message(id = 2, value = "retiredID")
String retiredID();
@Message(id = 3, value = "ignoreMeAlso")
String ignoreMeAlso();
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor.cases;
import org.apache.activemq.artemis.logs.annotation.LogBundle;
import org.apache.activemq.artemis.logs.annotation.Message;
// Test class used by LogAnnotationProcessorTest to check failure modes.
// Regex allows IDs 0-9, use higher value for a retiredIDs entry to check it fails.
@LogBundle(projectCode = "LAPTCase5", regexID="[0-9]{1}", retiredIDs = { 2, 4, 10})
public interface LAPTCase5_InvalidRetiredID {
@Message(id = 1, value = "ignoreMe")
String ignoreMe();
@Message(id = 3, value = "ignoreMeAlso")
String ignoreMeAlso();
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.logs.annotation.processor.cases;
import org.apache.activemq.artemis.logs.annotation.LogBundle;
import org.apache.activemq.artemis.logs.annotation.Message;
// Test class used by LogAnnotationProcessorTest to check failure modes.
// The retiredIDs must be sorted, provide some that are not. The error will give sorted version.
@LogBundle(projectCode = "LAPTCase6", regexID="[0-9]{1}", retiredIDs = { 2, 5, 4})
public interface LAPTCase6_UnsortedRetiredID {
@Message(id = 1, value = "ignoreMe")
String ignoreMe();
@Message(id = 3, value = "ignoreMeAlso")
String ignoreMeAlso();
}

View File

@ -578,6 +578,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.karuslabs</groupId>
<artifactId>elementary</artifactId>
<version>${elementary.version}</version>
<scope>test</scope>
</dependency>
<!-- Needed for Micrometer -->
<dependency>
<groupId>io.micrometer</groupId>

View File

@ -183,6 +183,7 @@
<jakarta.resource-api.version.alt>2.1.0</jakarta.resource-api.version.alt>
<!-- used on tests -->
<elementary.version>3.0.0</elementary.version>
<groovy.version>4.0.24</groovy.version>
<hadoop.minikdc.version>3.4.1</hadoop.minikdc.version>
<mockserver.version>5.15.0</mockserver.version>