View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.buildcache;
20  
21  import java.io.File;
22  import java.nio.file.Paths;
23  import java.util.Arrays;
24  import java.util.List;
25  import java.util.stream.Collectors;
26  
27  import org.apache.commons.lang3.SystemUtils;
28  import org.apache.maven.buildcache.xml.CacheConfig;
29  import org.apache.maven.buildcache.xml.build.CompletedExecution;
30  import org.apache.maven.buildcache.xml.build.PropertyValue;
31  import org.apache.maven.buildcache.xml.config.TrackedProperty;
32  import org.apache.maven.execution.scope.internal.MojoExecutionScope;
33  import org.apache.maven.plugin.MavenPluginManager;
34  import org.apache.maven.plugin.MojoExecution;
35  import org.apache.maven.project.MavenProject;
36  import org.jetbrains.annotations.NotNull;
37  import org.junit.jupiter.api.BeforeEach;
38  import org.junit.jupiter.api.Nested;
39  import org.junit.jupiter.api.Test;
40  import org.testcontainers.shaded.org.apache.commons.lang3.tuple.Pair;
41  
42  import static org.junit.jupiter.api.Assertions.assertFalse;
43  import static org.junit.jupiter.api.Assertions.assertTrue;
44  import static org.mockito.Mockito.mock;
45  import static org.mockito.Mockito.when;
46  
47  class BuildCacheMojosExecutionStrategyTest {
48  
49      @Nested
50      class ParametersMatchingTest {
51  
52          private BuildCacheMojosExecutionStrategy strategy;
53          private MavenProject projectMock;
54          private MojoExecution executionMock;
55          private CompletedExecution cacheRecordMock;
56          private CacheConfig cacheConfigMock;
57  
58          @BeforeEach
59          void setUp() {
60              cacheConfigMock = mock(CacheConfig.class);
61              strategy = new BuildCacheMojosExecutionStrategy(
62                      mock(CacheController.class),
63                      cacheConfigMock,
64                      mock(MojoParametersListener.class),
65                      mock(LifecyclePhasesHelper.class),
66                      mock(MavenPluginManager.class),
67                      mock(MojoExecutionScope.class));
68  
69              projectMock = mock(MavenProject.class);
70              executionMock = mock(MojoExecution.class);
71              cacheRecordMock = mock(CompletedExecution.class);
72          }
73  
74          @Test
75          void testBasicParamsMatching() {
76  
77              boolean windows = SystemUtils.IS_OS_WINDOWS;
78  
79              List<Pair<TrackedProperty, PropertyValue>> cacheProperties = Arrays.asList(
80                      setupProperty("bool", "true"),
81                      setupProperty("primitive", "1"),
82                      setupProperty("file", "c"),
83                      setupProperty("path", windows ? "..\\d\\e" : "../d/e"),
84                      setupProperty("list", "[a, b, c]"),
85                      setupProperty("array", "{c,d,e}"),
86                      setupProperty("nullObject", null));
87  
88              List<TrackedProperty> trackedProperties =
89                      cacheProperties.stream().map(Pair::getLeft).collect(Collectors.toList());
90  
91              List<PropertyValue> cacheRecordProperties =
92                      cacheProperties.stream().map(Pair::getRight).collect(Collectors.toList());
93  
94              when(cacheConfigMock.getTrackedProperties(executionMock)).thenReturn(trackedProperties);
95              when(cacheRecordMock.getProperties()).thenReturn(cacheRecordProperties);
96  
97              when(projectMock.getBasedir()).thenReturn(windows ? new File("c:\\a\\b") : new File("/a/b"));
98  
99              TestMojo testMojo = TestMojo.create(
100                     true,
101                     1,
102                     windows
103                             ? Paths.get("c:\\a\\b\\c").toFile()
104                             : Paths.get("/a/b/c").toFile(),
105                     Paths.get(windows ? "..\\d\\e" : "../d/e"),
106                     Arrays.<String>asList("a", "b", "c"),
107                     new String[] {"c", "d", "e"});
108 
109             assertTrue(strategy.isParamsMatched(projectMock, executionMock, testMojo, cacheRecordMock));
110         }
111 
112         @Test
113         void testSkipValue() {
114 
115             String propertyName = "anyObject";
116 
117             TrackedProperty config = new TrackedProperty();
118             config.setPropertyName(propertyName);
119             config.setSkipValue("true");
120 
121             // cache is better - not skipped
122             PropertyValue cache = new PropertyValue();
123             cache.setName(propertyName);
124             cache.setValue("false");
125 
126             when(cacheConfigMock.getTrackedProperties(executionMock)).thenReturn(Arrays.asList(config));
127             when(cacheRecordMock.getProperties()).thenReturn(Arrays.asList(cache));
128 
129             when(projectMock.getBasedir()).thenReturn(new File("."));
130 
131             // emulating that current build is "skipping" something and literal value is different from the cache
132             TestMojo testMojo = new TestMojo();
133             testMojo.setAnyObject("true");
134 
135             assertTrue(
136                     strategy.isParamsMatched(projectMock, executionMock, testMojo, cacheRecordMock),
137                     "If property set to 'skipValue' mismatch could be ignored because cached build"
138                             + " is more complete than requested build");
139         }
140 
141         @Test
142         void testDefaultValue() {
143 
144             String propertyName = "anyObject";
145 
146             TrackedProperty config = new TrackedProperty();
147             config.setPropertyName(propertyName);
148             config.setDefaultValue("defaultValue");
149 
150             // value was not cached
151             PropertyValue cache = new PropertyValue();
152             cache.setName(propertyName);
153             cache.setValue(null);
154 
155             when(cacheConfigMock.getTrackedProperties(executionMock)).thenReturn(Arrays.asList(config));
156             when(cacheRecordMock.getProperties()).thenReturn(Arrays.asList(cache));
157 
158             when(projectMock.getBasedir()).thenReturn(new File("."));
159 
160             // emulating that current build is "skipping" something and literal value is different from the cache
161             TestMojo testMojo = new TestMojo();
162             testMojo.setAnyObject("defaultValue");
163 
164             assertTrue(
165                     strategy.isParamsMatched(projectMock, executionMock, testMojo, cacheRecordMock),
166                     "If property has defaultValue it must be matched even if cache record has no this field");
167         }
168 
169         @Test
170         void testMismatch() {
171 
172             String propertyName = "anyObject";
173 
174             TrackedProperty config = new TrackedProperty();
175             config.setPropertyName(propertyName);
176 
177             // value was not cached
178             PropertyValue cache = new PropertyValue();
179             cache.setName(propertyName);
180             cache.setValue("1");
181 
182             when(cacheConfigMock.getTrackedProperties(executionMock)).thenReturn(Arrays.asList(config));
183             when(cacheRecordMock.getProperties()).thenReturn(Arrays.asList(cache));
184 
185             when(projectMock.getBasedir()).thenReturn(new File("."));
186 
187             // emulating that current build is "skipping" something and literal value is different from the cache
188             TestMojo testMojo = new TestMojo();
189             testMojo.setAnyObject("2");
190 
191             assertFalse(strategy.isParamsMatched(projectMock, executionMock, testMojo, cacheRecordMock));
192         }
193 
194         @NotNull
195         private Pair<TrackedProperty, PropertyValue> setupProperty(String propertyName, String value) {
196             TrackedProperty config = new TrackedProperty();
197             config.setPropertyName(propertyName);
198 
199             PropertyValue cache = new PropertyValue();
200             cache.setName(propertyName);
201             cache.setValue(value);
202 
203             return Pair.of(config, cache);
204         }
205     }
206 }