/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed 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.drools.testcoverage.regression; import org.assertj.core.api.Assertions; import org.drools.compiler.TurtleTestCategory; import org.kie.api.time.SessionPseudoClock; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import org.kie.api.KieServices; import org.kie.api.builder.KieBuilder; import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.Message; import org.kie.api.builder.ReleaseId; import org.kie.api.builder.model.KieBaseModel; import org.kie.api.builder.model.KieModuleModel; import org.kie.api.conf.EventProcessingOption; import org.kie.api.definition.type.FactType; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.runtime.conf.ClockTypeOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; /** * Reproducer for BZ 1181584, by Mike Wilson. */ public class DroolsGcCausesNPETest { private static final Logger LOGGER = LoggerFactory.getLogger(DroolsGcCausesNPETest.class); private static final String KIE_BASE_NAME = "defaultBase"; private static final String DRL_FILE_NAME = "DroolsGcCausesNPE.drl"; private static final KieServices SERVICES = KieServices.Factory.get(); private static final ReleaseId RELEASE_ID = SERVICES.newReleaseId( "org.drools.testcoverage", "drools-gc-causes-npe-example", "1.0"); private KieSession session; private SessionPseudoClock clock; private FactType eventFactType; @BeforeClass public static void beforeClass() throws Exception { final KieModuleModel module = SERVICES.newKieModuleModel(); final KieBaseModel base = module.newKieBaseModel(KIE_BASE_NAME); base.setEventProcessingMode(EventProcessingOption.STREAM); final KieFileSystem fs = SERVICES.newKieFileSystem(); fs.generateAndWritePomXML(RELEASE_ID); fs.write(SERVICES.getResources() .newClassPathResource(DRL_FILE_NAME, DroolsGcCausesNPETest.class)); fs.writeKModuleXML(module.toXML()); final KieBuilder builder = SERVICES.newKieBuilder(fs); final List<Message> errors = builder.buildAll().getResults() .getMessages(Message.Level.ERROR); Assertions.assertThat(errors).as("Unexpected errors building drl: " + errors).isEmpty(); SERVICES.getRepository().addKieModule(builder.getKieModule()); } @Before public void setUp() throws Exception { final KieSessionConfiguration conf = SERVICES.newKieSessionConfiguration(); conf.setOption(ClockTypeOption.get("pseudo")); conf.setProperty("type", "stateful"); final KieContainer container = SERVICES.newKieContainer(RELEASE_ID); session = container.getKieBase(KIE_BASE_NAME).newKieSession(conf, SERVICES.newEnvironment()); clock = session.getSessionClock(); eventFactType = session.getKieBase().getFactType(this.getClass().getPackage().getName(), "Event"); } /** * The original test method reproducing NPE during event GC. * BZ 1181584 */ @Test @Category(TurtleTestCategory.class) public void testMoreTimesRepeated() throws Exception { final Random r = new Random(1); int i = 0; try { for (; i < 100000; i++) { insertAndAdvanceTime(i, r.nextInt(4000)); } } catch (NullPointerException e) { LOGGER.warn("failed at i = " + i); LOGGER.warn("fact count: " + session.getFactCount()); logActiveFacts(); Assertions.fail("NPE thrown - consider reopening BZ 1181584", e); } } /** * Deterministic variant of the previous test method that reliably illustrates BZ 1274696. */ @Test public void test() throws Exception { insertAndAdvanceTime(1, 4000); } private void insertAndAdvanceTime(final long id, final long millis) throws IllegalAccessException, InstantiationException { insert(createEvent(id)); advanceTime(millis); } private Object createEvent(final long id) throws IllegalAccessException, InstantiationException { final Object event = eventFactType.newInstance(); eventFactType.set(event, "id", id); return event; } private void advanceTime(final long millis) { clock.advanceTime(millis, TimeUnit.MILLISECONDS); session.fireAllRules(); } private void insert(final Object event) { session.insert(event); session.fireAllRules(); } private void logActiveFacts() { LOGGER.warn("facts: "); session.getFactHandles().stream().map(Object::toString).forEach(LOGGER::warn); } }