/* * Copyright (C) 2015 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.jboss.errai.ui.nav.rebind; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.when; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jboss.errai.codegen.exception.GenerationException; import org.jboss.errai.codegen.meta.HasAnnotations; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.impl.java.JavaReflectionClass; import org.jboss.errai.common.client.api.IsElement; import org.jboss.errai.common.client.dom.HTMLElement; import org.jboss.errai.config.util.ClassScanner; import org.jboss.errai.ioc.rebind.ioc.bootstrapper.IOCProcessingContext; import org.jboss.errai.ioc.rebind.ioc.graph.api.InjectionSite; import org.jboss.errai.ioc.rebind.ioc.graph.api.Qualifier; import org.jboss.errai.ioc.rebind.ioc.graph.api.QualifierFactory; import org.jboss.errai.ioc.rebind.ioc.graph.impl.FactoryNameGenerator; import org.jboss.errai.ioc.rebind.ioc.graph.impl.InjectableHandle; import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectableProvider; import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectionContext; import org.jboss.errai.ui.nav.client.local.DefaultPage; import org.jboss.errai.ui.nav.client.local.Page; import org.jboss.errai.ui.nav.client.local.TransitionToRole; import org.jboss.errai.ui.nav.client.local.UniquePageRole; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.google.gwt.core.ext.GeneratorContext; /** * @author edewit@redhat.com */ @RunWith(PowerMockRunner.class) @PrepareForTest(ClassScanner.class) public class ValidationRulesTest { private final NavigationGraphGenerator generator = new NavigationGraphGenerator(); private TransitionProviderIOCExtension transitionProvider; @Mock private InjectionContext injContext; @Mock private GeneratorContext genContext; @Mock private IOCProcessingContext procContext; @Mock private InjectionSite injSite; @Mock private FactoryNameGenerator nameGenerator; @Mock private Qualifier transitionToRole; @Mock private Qualifier transitionTo; @Mock private org.jboss.errai.ui.nav.client.local.api.TransitionToRole toRoleAnno; @Captor private ArgumentCaptor<InjectableHandle> handleCaptor; @Captor private ArgumentCaptor<InjectableProvider> providerCaptor; @SuppressWarnings({ "rawtypes", "unchecked" }) @Before public void setup() { transitionProvider = new TransitionProviderIOCExtension(); final QualifierFactory qualFactory = mock(QualifierFactory.class); when(nameGenerator.generateFor(Mockito.any(), Mockito.any())).thenReturn("FactoryName"); when(procContext.getGeneratorContext()).thenReturn(genContext); when(injContext.getProcessingContext()).thenReturn(procContext); when(injContext.getQualifierFactory()).thenReturn(qualFactory); when(toRoleAnno.annotationType()).thenReturn((Class) org.jboss.errai.ui.nav.client.local.api.TransitionToRole.class); when(qualFactory.forSource(Mockito.any())).then(invocation -> { final HasAnnotations hasAnno = (HasAnnotations) invocation.getArguments()[0]; if (hasAnno.isAnnotationPresent(org.jboss.errai.ui.nav.client.local.api.TransitionToRole.class)) { return transitionToRole; } else if (hasAnno.isAnnotationPresent(org.jboss.errai.ui.nav.client.local.api.TransitionTo.class)) { return transitionTo; } else { throw new IllegalStateException(); } }); doNothing().when(injContext).registerExactTypeInjectableProvider(handleCaptor.capture(), providerCaptor.capture()); } @Test public void shouldThrowExceptionWhenMoreThenOneDefaultPage() { // given mockClassScanner(StartPage1.class, StartPage2.class); // when try { generator.generate(null, null); fail("GenerationException should have been thrown because more then one start page was defined."); } catch (final GenerationException e) { // then final String message = e.getMessage(); assertTrue(message.contains(StartPage1.class.getName())); assertTrue(message.contains(StartPage2.class.getName())); } verify(ClassScanner.class); } private List<MetaClass> createMetaClassList(final Class<?>... classes) { final List<MetaClass> result = new ArrayList<MetaClass>(classes.length); for (final Class<?> aClass : classes) { result.add(JavaReflectionClass.newInstance(aClass)); } return result; } @Test public void shouldThrowExceptionMoreUniquePages() { // given mockClassScanner(Page1.class, Page2.class); // when try { generator.generate(null, null); fail("GenerationException should have been thrown because more then one unique page was defined"); } catch (final GenerationException e) { final String message = e.getMessage(); assertTrue(message.contains(Page1.class.getName())); assertTrue(message.contains(Page2.class.getName())); } verify(ClassScanner.class); } @Test public void shouldThrowExceptionWhenNoStartPageDefined() { // given mockClassScanner(Page2.class); // when try { generator.generate(null, null); fail("GenerationException should have been thrown because no default start page was defined"); } catch (final GenerationException e) { final String message = e.getMessage(); assertTrue(message.contains("DefaultPage")); } verify(ClassScanner.class); } @Test(expected = GenerationException.class) public void shouldThrowExceptionWhenTransitionToForRoleWithNoPage() throws Exception { mockClassScanner(StartPage1.class, PageWithTransitionToMyUniquePageRole.class); generator.generate(null, null); fail("GenerationException should have been thrown because no PageWithTransitionToMyUniquePageRole was defined."); } @Test public void doNotValidateIfOnlyBlacklistedPages() throws Exception { overrideBlacklistedClassNames(BlacklistedPage.class.getCanonicalName()); mockClassScanner(BlacklistedPage.class); try { generator.generate(null, null); } catch (final GenerationException e) { fail("Validation should not have ocurred."); } } @Test(expected = GenerationException.class) public void shouldThrowExceptionForDefaultPageWithPathParam() throws Exception { mockClassScanner(DefaultPageWithPathParam.class); generator.generate(null, null); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test(expected = GenerationException.class) public void shouldThrowExceptionWithTransitionToMissingRole() throws Exception { mockClassScanner(); transitionProvider.afterInitialization(procContext, injContext); final InjectableProvider transitionToRoleProvider = getTransitionToRoleProvider(); when(injSite.isAnnotationPresent(org.jboss.errai.ui.nav.client.local.api.TransitionToRole.class)).thenReturn(true); when(injSite.getAnnotation(org.jboss.errai.ui.nav.client.local.api.TransitionToRole.class)).thenReturn(toRoleAnno); when(toRoleAnno.value()).thenReturn((Class) MyUniquePageRole.class); transitionToRoleProvider.getInjectable(injSite, nameGenerator); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test(expected = GenerationException.class) public void shouldThrowExceptionWithTransitionToUniqueRoleWithMultiplePages() throws Exception { mockClassScanner(Page1.class, Page2.class); transitionProvider.afterInitialization(procContext, injContext); final InjectableProvider transitionToRoleProvider = getTransitionToRoleProvider(); when(injSite.isAnnotationPresent(org.jboss.errai.ui.nav.client.local.api.TransitionToRole.class)).thenReturn(true); when(injSite.getAnnotation(org.jboss.errai.ui.nav.client.local.api.TransitionToRole.class)).thenReturn(toRoleAnno); when(toRoleAnno.value()).thenReturn((Class) MyUniquePageRole.class); transitionToRoleProvider.getInjectable(injSite, nameGenerator); } private InjectableProvider getTransitionToRoleProvider() { final List<InjectableHandle> handles = handleCaptor.getAllValues(); for (int i = 0; i < handles.size(); i++) { final InjectableHandle handle = handles.get(i); if (handle.getQualifier() == transitionToRole) { return providerCaptor.getAllValues().get(i); } } throw new IllegalStateException(); } private void mockClassScanner(final Class<?>... pages) { PowerMockito.mockStatic(ClassScanner.class); when(ClassScanner.getTypesAnnotatedWith(Page.class, null)).thenReturn(createMetaClassList(pages)); } private void overrideBlacklistedClassNames(final String... names) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { final Field blacklistedField = NavigationGraphGenerator.class.getDeclaredField("BLACKLISTED_PAGES"); blacklistedField.setAccessible(true); // Change the field to not be final so that we can overwrite it. final Field fieldModifiers = Field.class.getDeclaredField("modifiers"); fieldModifiers.setAccessible(true); fieldModifiers.setInt(blacklistedField, fieldModifiers.getInt(blacklistedField) & ~Modifier.FINAL); blacklistedField.set(null, Arrays.asList(names)); } @Page(role = DefaultPage.class) private static class StartPage1 implements IsElement { @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); } } @Page(role = DefaultPage.class) private static class StartPage2 implements IsElement { @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); }} private static class MyUniquePageRole implements UniquePageRole {} @Page(role = {ValidationRulesTest.MyUniquePageRole.class, DefaultPage.class}) private static class Page1 implements IsElement { @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); }} @Page(role = ValidationRulesTest.MyUniquePageRole.class) private static class Page2 implements IsElement { @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); }} @Page private static class PageWithTransitionToMyUniquePageRole implements IsElement { @SuppressWarnings("unused") private TransitionToRole<MyUniquePageRole> transition; @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); } } @Page private static class BlacklistedPage implements IsElement { @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); } } @Page(role = DefaultPage.class, path = "{var}/text") private static class DefaultPageWithPathParam implements IsElement { @Override public HTMLElement getElement() { throw new RuntimeException("Not yet implemented."); }} }