Skip to content

Commit 28cb0c3

Browse files
committed
CpsScriptTest: add methodTooLargeExceptionFabricated() and methodTooLargeExceptionRealistic() tests
Signed-off-by: Jim Klimov <[email protected]>
1 parent b91c7f6 commit 28cb0c3

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

plugin/src/test/java/org/jenkinsci/plugins/workflow/cps/CpsScriptTest.java

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,112 @@ public void evaluateShallSandbox() throws Exception {
8484
r.assertLogContains("Scripts not permitted to use method groovy.lang.Script run java.io.File java.lang.String[]", b);
8585
}
8686

87+
@Test public void methodTooLargeExceptionFabricated() throws Exception {
88+
// Fabricate a MethodTooLargeException which "normally" happens when evaluated
89+
// groovy script becomes a Java class too large for Java to handle internally.
90+
// In Jenkins practice this can happen not only due to large singular pipelines
91+
// (one big nudge to offload code into shared libraries), but was also seen due
92+
// to heavy nesting of exception handling and other loops (simple refactoring
93+
// can help).
94+
WorkflowJob p = r.createProject(WorkflowJob.class);
95+
// sandbox == false to allow creation of the exception here:
96+
p.setDefinition(new CpsFlowDefinition(
97+
"import groovyjarjarasm.asm.MethodTooLargeException;\n\n" +
98+
"throw new MethodTooLargeException('className', 'methodName', 'methodDescriptor', 65535);"
99+
, false));
100+
WorkflowRun b = r.buildAndAssertStatus(Result.FAILURE, p);
101+
r.assertLogContains("groovyjarjarasm.asm.MethodTooLargeException: Method too large: className.methodName methodDescriptor", b);
102+
r.assertLogContains("at WorkflowScript.run(WorkflowScript:3)", b);
103+
r.assertLogContains("at ___cps.transform___(Native Method)", b);
104+
}
105+
106+
@Test public void methodTooLargeExceptionRealistic() throws Exception {
107+
// See comments above. Here we try to really induce a "method too large"
108+
// condition by abusing the nesting of exception-handling, too many stages
109+
// or methods, and whatever else we can throw at it.
110+
WorkflowJob p = r.createProject(WorkflowJob.class);
111+
StringBuffer sbMethods = new StringBuffer();
112+
StringBuffer sbStages = new StringBuffer();
113+
int i, max = 255;
114+
115+
for (i = 0; i < 250; i++) {
116+
// Up to 255 stages allowed
117+
sbStages.append("stage('Stage " + i + "') { steps { method" + i + "(); } }\n");
118+
}
119+
120+
for (i = 0; i < max; i++) {
121+
sbMethods.append("def method" + i + "() { echo 'i = " + i + "'; }\n");
122+
}
123+
124+
sbMethods.append("def method() {\n");
125+
for (i = 0; i < max; i++) {
126+
sbMethods.append("try { // " + i + "\n");
127+
}
128+
sbMethods.append(" Integer x = 'zzz'; // incur conversion exception\n");
129+
for (i = 0; i < max; i++) {
130+
sbMethods.append("} catch (Throwable t) { // " + i + "\n method" + i + "(); throw t; }\n");
131+
}
132+
sbMethods.append("}\n");
133+
134+
p.setDefinition(new CpsFlowDefinition(sbMethods.toString() +
135+
"pipeline {\n" +
136+
" agent none;\n" +
137+
" stages {\n" +
138+
" stage ('Test stage') {\n" +
139+
" steps {\n" +
140+
" script {\n" +
141+
" echo 'BEGINNING TEST IN PIPELINE';\n" +
142+
" method();\n" +
143+
" echo 'ENDED TEST IN PIPELINE';\n" +
144+
" }\n" +
145+
" }\n" +
146+
" }\n" +
147+
sbStages.toString() +
148+
" }\n" +
149+
"}\n" +
150+
"//echo 'BEGINNING TEST OUT OF PIPELINE';\n" +
151+
"//method();\n" +
152+
"//echo 'ENDED TEST OUT OF PIPELINE';\n"
153+
, true));
154+
155+
WorkflowRun b = p.scheduleBuild2(0).get();
156+
157+
// DEV-TEST // System.out.println(b.getLog());
158+
159+
r.assertLogContains("MethodTooLargeException", b);
160+
161+
/*
162+
// Report as of release 3880.vb_ef4b_5cfd270 (Feb 2024)
163+
// and same pattern seen since at least Jun 2022 (note
164+
// that numbers after ___cps___ differ from job to job):
165+
166+
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
167+
General error during class generation: Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
168+
169+
groovyjarjarasm.asm.MethodTooLargeException: Method too large: WorkflowScript.___cps___1 ()Lcom/cloudbees/groovy/cps/impl/CpsFunction;
170+
at groovyjarjarasm.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2087)
171+
at groovyjarjarasm.asm.ClassWriter.toByteArray(ClassWriter.java:447)
172+
at org.codehaus.groovy.control.CompilationUnit$17.call(CompilationUnit.java:850)
173+
at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087)
174+
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
175+
at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
176+
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
177+
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
178+
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
179+
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
180+
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:190)
181+
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:175)
182+
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:637)
183+
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:583)
184+
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
185+
at hudson.model.ResourceController.execute(ResourceController.java:101)
186+
at hudson.model.Executor.run(Executor.java:442)
187+
*/
188+
189+
r.assertLogContains("Method too large: WorkflowScript.___cps___", b);
190+
r.assertLogContains("()Lcom/cloudbees/groovy/cps/impl/CpsFunction;", b);
191+
192+
// Assert separately from (and after) log parsing, to facilitate test maintenance
193+
r.assertBuildStatus(Result.FAILURE, b);
194+
}
87195
}

0 commit comments

Comments
 (0)