| /* |
| * 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 SF 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.felix.hc.core.impl; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.argThat; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import java.util.Arrays; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.felix.hc.api.HealthCheck; |
| import org.apache.felix.hc.api.Result; |
| import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions; |
| import org.apache.felix.hc.api.execution.HealthCheckExecutionResult; |
| import org.apache.felix.hc.api.execution.HealthCheckExecutor; |
| import org.apache.felix.hc.api.execution.HealthCheckMetadata; |
| import org.apache.felix.hc.api.execution.HealthCheckSelector; |
| import org.apache.felix.hc.core.impl.executor.ExecutionResult; |
| import org.apache.felix.hc.core.impl.util.HealthCheckFilter; |
| import org.hamcrest.Description; |
| import org.hamcrest.Matcher; |
| import org.hamcrest.TypeSafeMatcher; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.mockito.ArgumentMatcher; |
| import org.mockito.Matchers; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.Spy; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.component.ComponentConstants; |
| import org.osgi.service.component.ComponentContext; |
| |
| public class CompositeHealthCheckTest { |
| |
| @Spy |
| private CompositeHealthCheck compositeHealthCheck = new CompositeHealthCheck(); |
| |
| @Mock |
| private HealthCheckExecutor healthCheckExecutor; |
| |
| @Mock |
| private ComponentContext componentContext; |
| |
| @Before |
| public void setup() { |
| MockitoAnnotations.initMocks(this); |
| compositeHealthCheck.setHealthCheckExecutor(healthCheckExecutor); |
| compositeHealthCheck.setFilterTags(new String[] {}); |
| compositeHealthCheck.setComponentContext(componentContext); |
| } |
| |
| @Test |
| public void testExecution() { |
| |
| doReturn((Result) null).when(compositeHealthCheck).checkForRecursion(Matchers.<ServiceReference> any(), |
| Matchers.<Set<String>> any()); |
| String[] testTags = new String[] { "tag1" }; |
| compositeHealthCheck.setFilterTags(testTags); |
| |
| List<HealthCheckExecutionResult> executionResults = new LinkedList<HealthCheckExecutionResult>(); |
| executionResults.add(createExecutionResult("Check 1", testTags, new Result(Result.Status.OK, "Good"))); |
| executionResults.add(createExecutionResult("Check 2", testTags, new Result(Result.Status.CRITICAL, "Bad"))); |
| |
| when(healthCheckExecutor.execute(any(HealthCheckSelector.class), any(HealthCheckExecutionOptions.class))) |
| .thenReturn(executionResults); |
| |
| Result result = compositeHealthCheck.execute(); |
| |
| verify(healthCheckExecutor, times(1)).execute(argThat(selectorWithTags(testTags)), argThat(andOptions)); |
| |
| assertEquals(Result.Status.CRITICAL, result.getStatus()); |
| |
| } |
| |
| private ArgumentMatcher<HealthCheckSelector> selectorWithTags(final String[] tags) { |
| return new ArgumentMatcher<HealthCheckSelector>() { |
| @Override |
| public boolean matches(HealthCheckSelector healthCheckSelector) { |
| return Arrays.equals(healthCheckSelector.tags(), tags) && healthCheckSelector.names() == null; |
| } |
| |
| @Override |
| public String toString() { |
| return "a select with tags (" + Arrays.toString(tags) + ") and no names."; |
| } |
| }; |
| } |
| |
| private HealthCheckExecutionResult createExecutionResult(String name, String[] testTags, Result result) { |
| HealthCheckExecutionResult healthCheckExecutionResult = new ExecutionResult( |
| new HealthCheckMetadata(new DummyHcServiceReference(name, testTags, |
| new String[0])), |
| result, 0L); |
| return healthCheckExecutionResult; |
| } |
| |
| @Test |
| public void testSimpleRecursion() { |
| |
| // composite check referencing itself |
| final String[] filterTags = new String[] { "check1" }; |
| final DummyHcServiceReference hcRef = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags); |
| |
| // test check is hcRef |
| doReturn(hcRef).when(componentContext).getServiceReference(); |
| compositeHealthCheck.setFilterTags(filterTags); |
| |
| compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) { |
| |
| @Override |
| public ServiceReference[] getHealthCheckServiceReferences(HealthCheckSelector selector) { |
| String[] tags = selector.tags(); |
| ServiceReference[] result = new ServiceReference[] {}; |
| if (tags.length > 0) { |
| if (tags[0].equals(filterTags[0])) { |
| result = new ServiceReference[] { hcRef }; |
| } |
| } |
| return result; |
| } |
| |
| }); |
| |
| Result result = compositeHealthCheck.execute(); |
| |
| verify(healthCheckExecutor, never()).execute(any(HealthCheckSelector.class)); |
| assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus()); |
| } |
| |
| @Test |
| public void testCyclicRecursion() { |
| |
| // three checks, cyclic |
| final String[] filterTags = new String[] { "check2" }; |
| final DummyHcServiceReference hcRef1 = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags); |
| final DummyHcServiceReference hcRef2 = new DummyHcServiceReference("Check 2", new String[] { "check2" }, new String[] { "check3" }); |
| final DummyHcServiceReference hcRef3 = new DummyHcServiceReference("Check 3", new String[] { "check3" }, new String[] { "check1" }); |
| |
| // test check is hcRef1 |
| doReturn(hcRef1).when(componentContext).getServiceReference(); |
| compositeHealthCheck.setFilterTags(filterTags); |
| |
| compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) { |
| |
| @Override |
| public ServiceReference[] getHealthCheckServiceReferences(HealthCheckSelector selector, boolean combineTagsWithOr) { |
| String[] tags = selector.tags(); |
| ServiceReference[] result = new ServiceReference[] {}; |
| if (tags.length > 0) { |
| if (tags[0].equals(filterTags[0])) { |
| result = new ServiceReference[] { hcRef2 }; |
| } else if (tags[0].equals("check3")) { |
| result = new ServiceReference[] { hcRef3 }; |
| } else if (tags[0].equals("check1")) { |
| result = new ServiceReference[] { hcRef1 }; |
| } |
| } |
| |
| return result; |
| } |
| |
| }); |
| |
| Result result = compositeHealthCheck.execute(); |
| |
| verify(healthCheckExecutor, never()).execute(any(HealthCheckSelector.class)); |
| assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus()); |
| } |
| |
| @Test |
| public void testCombineWithOr() { |
| |
| // composite check referencing itself |
| final String[] filterTags = new String[] { "check1" }; |
| compositeHealthCheck.setFilterTags(filterTags); |
| compositeHealthCheck.setCombineTagsWithOr(true); |
| |
| compositeHealthCheck.execute(); |
| |
| verify(healthCheckExecutor, times(1)).execute(argThat(selectorWithTags(filterTags)), argThat(orOptions)); |
| } |
| |
| private ArgumentMatcher<HealthCheckExecutionOptions> orOptions = new ArgumentMatcher<HealthCheckExecutionOptions>() { |
| @Override |
| public boolean matches(HealthCheckExecutionOptions options) { |
| return options.isCombineTagsWithOr(); |
| } |
| |
| @Override |
| public String toString() { |
| return "options combining tags with or."; |
| } |
| }; |
| |
| private ArgumentMatcher<HealthCheckExecutionOptions> andOptions = new ArgumentMatcher<HealthCheckExecutionOptions>() { |
| @Override |
| public boolean matches(HealthCheckExecutionOptions options) { |
| return !options.isCombineTagsWithOr(); |
| } |
| |
| @Override |
| public String toString() { |
| return "options combining tags with and."; |
| } |
| }; |
| |
| private static class DummyHcServiceReference implements ServiceReference { |
| |
| private long id; |
| private String name; |
| private String[] tags; |
| private String[] filterTags; |
| |
| public DummyHcServiceReference(String name, String[] tags, String[] filterTags) { |
| super(); |
| this.id = (long) (Math.random() * Long.MAX_VALUE); |
| this.name = name; |
| this.tags = tags; |
| this.filterTags = filterTags; |
| } |
| |
| @Override |
| public Object getProperty(String key) { |
| |
| if (Constants.SERVICE_ID.equals(key)) { |
| return id; |
| } else if (HealthCheck.NAME.equals(key)) { |
| return name; |
| } else if (HealthCheck.MBEAN_NAME.equals(key)) { |
| return name; |
| } else if (HealthCheck.TAGS.equals(key)) { |
| return tags; |
| } else if (CompositeHealthCheck.PROP_FILTER_TAGS.equals(key)) { |
| return filterTags; |
| } else if (ComponentConstants.COMPONENT_NAME.equals(key)) { |
| return filterTags != null ? CompositeHealthCheck.class.getName() : "some.other.HealthCheck"; |
| } else { |
| return null; |
| } |
| } |
| |
| @Override |
| public String[] getPropertyKeys() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Bundle getBundle() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Bundle[] getUsingBundles() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean isAssignableTo(Bundle bundle, String className) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int compareTo(Object reference) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| } |
| } |