Skip to content

Commit

Permalink
(@StuartR) Implementing View Mask Filter.
Browse files Browse the repository at this point in the history
JENKINS-30622
JENKINS-28225
  • Loading branch information
p4paul committed Aug 22, 2016
1 parent e284913 commit a4b9b3a
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 56 deletions.
@@ -0,0 +1,33 @@
package org.jenkinsci.plugins.p4.filters;

import hudson.Extension;

import java.io.Serializable;

import org.kohsuke.stapler.DataBoundConstructor;

public class FilterViewMaskImpl extends Filter implements Serializable {

private static final long serialVersionUID = 1L;

private final String viewMask;

@DataBoundConstructor
public FilterViewMaskImpl(String viewMask) {
this.viewMask = viewMask;
}

public String getViewMask() {
return viewMask;
}

@Extension
public static final class DescriptorImpl extends FilterDescriptor {

@Override
public String getDisplayName() {
return "Exclude changes outside view mask";
}

}
}
61 changes: 46 additions & 15 deletions src/main/java/org/jenkinsci/plugins/p4/tasks/PollTask.java
@@ -1,30 +1,29 @@
package org.jenkinsci.plugins.p4.tasks;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.perforce.p4java.core.file.IFileSpec;
import com.perforce.p4java.exception.AccessException;
import com.perforce.p4java.exception.RequestException;
import com.perforce.p4java.impl.generic.core.Changelist;
import hudson.FilePath.FileCallable;
import hudson.remoting.VirtualChannel;
import jenkins.security.Roles;
import org.jenkinsci.plugins.p4.changes.P4Revision;
import org.jenkinsci.plugins.p4.client.ClientHelper;
import org.jenkinsci.plugins.p4.filters.Filter;
import org.jenkinsci.plugins.p4.filters.FilterPathImpl;
import org.jenkinsci.plugins.p4.filters.FilterPerChangeImpl;
import org.jenkinsci.plugins.p4.filters.FilterPollMasterImpl;
import org.jenkinsci.plugins.p4.filters.FilterUserImpl;
import org.jenkinsci.plugins.p4.filters.FilterViewMaskImpl;
import org.jenkinsci.remoting.RoleChecker;
import org.jenkinsci.remoting.RoleSensitive;

import com.perforce.p4java.core.file.IFileSpec;
import com.perforce.p4java.exception.AccessException;
import com.perforce.p4java.exception.RequestException;
import com.perforce.p4java.impl.generic.core.Changelist;

import hudson.FilePath.FileCallable;
import hudson.remoting.VirtualChannel;
import jenkins.security.Roles;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PollTask extends AbstractTask implements FileCallable<List<Integer>>, Serializable {

Expand Down Expand Up @@ -150,6 +149,38 @@ private boolean filterChange(Changelist changelist, List<Filter> scmFilter) thro
return true;
}
}

// Scan through View Mask filters
if (f instanceof FilterViewMaskImpl) {
// at least one file in the change must be contained in the view mask
List<IFileSpec> included = new ArrayList<IFileSpec>();

String viewMask = ((FilterViewMaskImpl) f).getViewMask();
for (IFileSpec s : files) {
boolean isFileInViewMask = false;
String p = s.getDepotPathString();
for(String maskPath : viewMask.split("\n")) {
if (p.startsWith(maskPath)) {
isFileInViewMask = true;
}

if (maskPath.startsWith("-")) {
String excludedMaskPath = maskPath.substring(maskPath.indexOf("-") + 1);
if (p.startsWith(excludedMaskPath)) {
isFileInViewMask = false;
}
}
}

if(isFileInViewMask) {
included.add(s);
}
}

if (included.isEmpty()) {
return true;
}
}
}
return false;
}
Expand Down
@@ -0,0 +1,8 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">

<f:entry title="${%Perforce View Mask}" field="viewMask">
<f:textarea/>
</f:entry>

</j:jelly>
@@ -0,0 +1,55 @@
<div>
<b>View Mask filter</b>
<p>Changes can be filtered to not trigger a build; if none of the files
within a change are contained in the view mask, the build is filtered.</p>
<p style="margin-left: 30.0px;">
For example, with a View Mask Filter of:
<br/>
<code style="margin-left: 60.0px;">//depot/main/tests</code>
<br/>
<code style="margin-left: 60.0px;">-//depot/main/tests/001</code>
<br/>
</p>
<p style="margin-left: 60.0px;">
<strong>Case A</strong> (change will not be filtered, as index.xml is
in the view mask):
</p>
<p style="margin-left: 90.0px;">Files:</p>
<ul>
<li style="margin-left: 90.0px;"><code>//depot/main/tests/index.xml</code>
</li>
<li style="margin-left: 90.0px;"><code>//depot/main/tests/001/test.xml</code>
</li>
</ul>
<p style="margin-left: 60.0px;">
<strong>Case B</strong> (change will not be filtered, as index.xml is
in the view mask):
</p>
<p style="margin-left: 90.0px;">Files:</p>
<ul>
<li style="margin-left: 90.0px;"><code>//depot/main/test/index.xml</code>
</li>
<li style="margin-left: 90.0px;"><code>//depot/main/src/build.xml</code>
</li>
</ul>
<p style="margin-left: 60.0px;">
<strong>Case C</strong> (change will be filtered, as no file is
in the view mask):
</p>
<p style="margin-left: 90.0px;">Files:</p>
<ul>
<li style="margin-left: 90.0px;"><code>//depot/main/src/build.xml</code>
</li>
</ul>
<p style="margin-left: 60.0px;">
<strong>Case D</strong> (change will be filtered, as no file is
in the view mask):
</p>
<p style="margin-left: 90.0px;">Files:</p>
<ul>
<li style="margin-left: 90.0px;"><code>//depot/main/src/build.xml</code>
</li>
<li style="margin-left: 90.0px;"><code>//depot/main/tests/001/test.xml</code>
</li>
</ul>
</div>
170 changes: 129 additions & 41 deletions src/test/java/org/jenkinsci/plugins/p4/client/ConnectionTest.java
@@ -1,20 +1,30 @@
package org.jenkinsci.plugins.p4.client;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.cloudbees.plugins.credentials.CredentialsDescriptor;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.perforce.p4java.Metadata;
import com.perforce.p4java.client.IClient;
import hudson.model.Action;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Cause;
import hudson.model.Cause.UserIdCause;
import hudson.model.Descriptor;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ParameterValue;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.StringParameterValue;
import hudson.scm.RepositoryBrowser;
import hudson.scm.SCMDescriptor;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.LogTaskListener;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.p4.DummyServer;
import org.jenkinsci.plugins.p4.P4Server;
import org.jenkinsci.plugins.p4.PerforceScm;
Expand All @@ -25,6 +35,7 @@
import org.jenkinsci.plugins.p4.credentials.P4PasswordImpl;
import org.jenkinsci.plugins.p4.filters.Filter;
import org.jenkinsci.plugins.p4.filters.FilterPerChangeImpl;
import org.jenkinsci.plugins.p4.filters.FilterViewMaskImpl;
import org.jenkinsci.plugins.p4.populate.AutoCleanImpl;
import org.jenkinsci.plugins.p4.populate.Populate;
import org.jenkinsci.plugins.p4.review.ReviewProp;
Expand All @@ -48,33 +59,20 @@
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import com.cloudbees.plugins.credentials.CredentialsDescriptor;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.perforce.p4java.Metadata;
import com.perforce.p4java.client.IClient;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import hudson.model.Action;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Cause;
import hudson.model.Descriptor;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.ParameterValue;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.StringParameterValue;
import hudson.model.Cause.UserIdCause;
import hudson.scm.RepositoryBrowser;
import hudson.scm.SCMDescriptor;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.LogTaskListener;
import net.sf.json.JSONObject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class ConnectionTest {

Expand Down Expand Up @@ -641,6 +639,96 @@ public void testPolling_Inc() throws Exception {
assertEquals(4, change);
}

@Test
public void testPolling_Mask() throws Exception {

String client = "manual.ws";
String stream = null;
String line = "LOCAL";
String view = "//depot/... //" + client + "/...";
WorkspaceSpec spec = new WorkspaceSpec(false, false, false, false, false, false, stream, line, view);

FreeStyleProject project = jenkins.createFreeStyleProject("Manual-Head");
ManualWorkspaceImpl workspace = new ManualWorkspaceImpl("none", false, client, spec);

Populate populate = new AutoCleanImpl(true, true, false, false, null, null);
List<Filter> filter = new ArrayList<Filter>();

// Filter changes outside of //depot/Data path
FilterViewMaskImpl mask = new FilterViewMaskImpl("//depot/Data");
filter.add(mask);
PerforceScm scm = new PerforceScm(credential, workspace, filter, populate, null);
project.setScm(scm);
project.save();

// Build at change 3
List<ParameterValue> list = new ArrayList<ParameterValue>();
list.add(new StringParameterValue(ReviewProp.STATUS.toString(), "submitted"));
list.add(new StringParameterValue(ReviewProp.CHANGE.toString(), "3"));
Action actions = new SafeParametersAction(new ArrayList<ParameterValue>(), list);

FreeStyleBuild build;
UserIdCause cause = new Cause.UserIdCause();
build = project.scheduleBuild2(0, cause, actions).get();
assertEquals(Result.SUCCESS, build.getResult());

// Poll for changes incrementally
LogTaskListener listener = new LogTaskListener(logger, Level.INFO);
project.poll(listener);
List<Integer> buildList = scm.getChanges();
assertEquals(2, buildList.size());
int change = buildList.get(0);
assertEquals(18, change);
}

@Test
public void testPolling_Mask_Excl() throws Exception {

String client = "manual.ws";
String stream = null;
String line = "LOCAL";
String view = "//depot/... //" + client + "/...";
WorkspaceSpec spec = new WorkspaceSpec(false, false, false, false, false, false, stream, line, view);

FreeStyleProject project = jenkins.createFreeStyleProject("Manual-Head");
ManualWorkspaceImpl workspace = new ManualWorkspaceImpl("none", false, client, spec);

Populate populate = new AutoCleanImpl(true, true, false, false, null, null);
List<Filter> filter = new ArrayList<Filter>();

// Filter changes outside of //depot/Main and also under //depot/Main/TPI-83
StringBuilder sb = new StringBuilder();
sb.append("//depot/Main");
sb.append("\n");
sb.append("-//depot/Main/TPI-83");

FilterViewMaskImpl mask = new FilterViewMaskImpl(sb.toString());
filter.add(mask);
PerforceScm scm = new PerforceScm(credential, workspace, filter, populate, null);
project.setScm(scm);
project.save();

// Build at change 3
List<ParameterValue> list = new ArrayList<ParameterValue>();
list.add(new StringParameterValue(ReviewProp.STATUS.toString(), "submitted"));
list.add(new StringParameterValue(ReviewProp.CHANGE.toString(), "3"));
Action actions = new SafeParametersAction(new ArrayList<ParameterValue>(), list);

FreeStyleBuild build;
UserIdCause cause = new Cause.UserIdCause();
build = project.scheduleBuild2(0, cause, actions).get();
assertEquals(Result.SUCCESS, build.getResult());

// TODO: determine the CL our mask should poll us at
// Poll for changes incrementally
LogTaskListener listener = new LogTaskListener(logger, Level.INFO);
project.poll(listener);
List<Integer> buildList = scm.getChanges();
assertEquals(13, buildList.size());
int change = buildList.get(0);
assertEquals(16, change);
}

@Test
public void testManual_Modtime() throws Exception {

Expand Down

0 comments on commit a4b9b3a

Please sign in to comment.