Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion box.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@
"log:lucee":"server log coldbox-lucee@5 --follow",
"log:boxlang":"server log coldbox-boxlang-cfml@1 --follow",
"log:boxlangprime":"server log coldbox-boxlang@1 --follow",
"log:adobe":"server log coldbox-adobe@2025 --follow"
"log:adobe":"server log coldbox-adobe@2025 --follow",
"perf:run":"task run tests/perf-harness/PerformanceSuite.cfc",
"perf:run:quick":"task run tests/perf-harness/PerformanceSuite.cfc engines=boxlang-cfml iterations=10 warmup=3 coldStart=false",
"perf:be":"task run tests/perf-harness/PerformanceSuite.cfc versions=be",
"perf:stable":"task run tests/perf-harness/PerformanceSuite.cfc versions=stable",
"perf:lucee":"task run tests/perf-harness/PerformanceSuite.cfc engines=lucee-7",
"perf:adobe":"task run tests/perf-harness/PerformanceSuite.cfc engines=adobe-2025"
}
}
847 changes: 847 additions & 0 deletions tests/perf-harness/PerformanceSuite.cfc

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions tests/perf-harness/app/config/ColdBox.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Shared ColdBox configuration for performance harness.
* appName and appKey are overridden per-version in be-app/config and stable-app/config.
*/
component {

function configure(){
variables.coldbox = {
appName : "ColdBoxPerfHarness",
eventName : "event",
reinitPassword : "",
reinitKey : "fwreinit",
handlersIndexAutoReload : false,
debugMode : false,
defaultEvent : "Main.index",
requestStartHandler : "",
requestEndHandler : "",
applicationStartHandler : "",
applicationEndHandler : "",
sessionStartHandler : "",
sessionEndHandler : "",
missingTemplateHandler : "",
applicationHelper : "",
viewsHelper : "",
modulesExternalLocation : [],
viewsExternalLocation : "",
layoutsExternalLocation : "",
handlersExternalLocation: "",
requestContextDecorator : "",
exceptionHandler : "",
invalidEventHandler : "",
customErrorTemplate : "",
handlerCaching : true,
eventCaching : false,
proxyReturnCollection : false
};

variables.layoutSettings = {
defaultLayout : "Main.cfm",
defaultView : ""
};

variables.modules = {
autoReload : false,
include : [ "perf-module" ],
exclude : []
};

variables.interceptors = [
{ class : "#appMapping#.interceptors.PerfInterceptor" }
];

variables.logBox = {
appenders : {
console : { class : "ConsoleAppender" }
},
root : { levelmax : "WARN", appenders : "*" }
};
}

}
40 changes: 40 additions & 0 deletions tests/perf-harness/app/config/Router.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Performance harness router — five measurable test scenarios.
*/
component {

/**
* ColdBox hook: called by RoutingService instead of reading CGI.PATH_INFO.
* When the Tuckey URL rewriter does a forward(), the original request URI
* is in the javax.servlet.forward.request_uri attribute; CGI.PATH_INFO is empty.
* This provider extracts the original path and strips the app sub-path prefix.
*/
function pathInfoProvider( event ){
var forwardURI = getPageContext().getRequest().getAttribute( "javax.servlet.forward.request_uri" )
if ( !isNull( forwardURI ) && len( forwardURI ) ) {
return reReplaceNoCase( forwardURI, "^/tests/perf-harness/(be-app|stable-app)", "" )
}
return CGI.PATH_INFO
}

function configure(){
// Health check — minimal response, no DI, no view
route( "/perf/health" ).to( "Main.health" )

// Simple view — renders view + layout
route( "/perf/view" ).to( "Main.index" )

// JSON API — DI injection + renderData
route( "/perf/api" ).to( "Api.list" )

// Complex view — multiple model injections + data loop
route( "/perf/complex" ).to( "Main.complex" )

// Module request — full HMVC module routing
route( "/perf/module" ).to( "perf-module:Items.index" )

// Default convention routing
route( "/:handler/:action?" ).end()
}

}
23 changes: 23 additions & 0 deletions tests/perf-harness/app/handlers/Api.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* REST API handler — tests DI + JSON serialization pipeline.
*/
component extends="coldbox.system.EventHandler" {

property name="userService" inject="UserService";

// JSON list — injects UserService and renders JSON
function list( event, rc, prc ){
var data = {
status : "success",
count : 10,
engine : server.keyExists( "coldfusion" ) ? "Adobe CF" : ( server.keyExists( "lucee" ) ? "Lucee" : "BoxLang" ),
users : userService.getUsers( 10 ),
metadata : {
generated : now(),
framework : "ColdBox"
}
}
event.renderData( type="json", data=data )
}

}
29 changes: 29 additions & 0 deletions tests/perf-harness/app/handlers/Main.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Main handler for performance test scenarios.
*/
component extends="coldbox.system.EventHandler" {

property name="userService" inject="UserService";
property name="productService" inject="ProductService";

// Baseline — no DI usage, no view, minimal processing
function health( event, rc, prc ){
event.renderData( type="text", data="ok", statusCode=200 )
}

// Simple view — renders main/index with layout
function index( event, rc, prc ){
prc.message = "ColdBox Performance Harness"
prc.timestamp = now()
prc.version = getColdBoxSetting( "version", "unknown" )
event.setView( "main/index" )
}

// Complex view — resolves two model dependencies, loops data
function complex( event, rc, prc ){
prc.users = userService.getUsers( 10 )
prc.products = productService.getProducts( 5 )
event.setView( "main/complex" )
}

}
19 changes: 19 additions & 0 deletions tests/perf-harness/app/interceptors/PerfInterceptor.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Records per-request timing into the PRC scope for debugging.
*/
component {

void function configure(){
}

void function preProcess( event, data, rc, prc ){
arguments.prc._perfStart = getTickCount()
}

void function postProcess( event, data, rc, prc ){
if( arguments.prc.keyExists( "_perfStart" ) ){
arguments.prc._perfElapsed = getTickCount() - arguments.prc._perfStart
}
}

}
23 changes: 23 additions & 0 deletions tests/perf-harness/app/layouts/Main.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ColdBox Perf Harness</title>
<style>
body { font-family: system-ui, sans-serif; margin: 2rem; color: #333; }
h2 { color: #0066cc; }
ul { line-height: 1.8; }
</style>
</head>
<body>
<cfoutput>
<header>
<h1>ColdBox Performance Harness</h1>
</header>
<main>
#renderView()#
</main>
</cfoutput>
</body>
</html>
24 changes: 24 additions & 0 deletions tests/perf-harness/app/models/ProductService.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Product service singleton — provides test product data.
*/
component singleton {

variables.CATEGORIES = [ "Electronics", "Clothing", "Books", "Food", "Tools" ]

function getProducts( numeric count=5 ){
var result = []
for( var i = 1; i <= arguments.count; i++ ){
result.append({
id : i,
name : "Product #i#",
sku : "SKU-#numberFormat( i, "00000" )#",
price : precisionEvaluate( i * 9.99 ),
category : variables.CATEGORIES[ ( ( i - 1 ) mod variables.CATEGORIES.len() ) + 1 ],
inStock : ( i mod 4 != 0 ),
tags : [ "tag#i#", "perf", "test" ]
})
}
return result
}

}
34 changes: 34 additions & 0 deletions tests/perf-harness/app/models/UserService.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* User service singleton — provides test user data.
*/
component singleton {

function getUsers( numeric count=10 ){
var result = []
for( var i = 1; i <= arguments.count; i++ ){
result.append({
id : i,
firstName : "User",
lastName : "Number#i#",
email : "user#i#@perf.test",
role : ( i mod 3 == 0 ) ? "admin" : "user",
active : true,
createdAt : now()
})
}
return result
}

function getUserById( required numeric id ){
return {
id : arguments.id,
firstName : "User",
lastName : "Number#arguments.id#",
email : "user#arguments.id#@perf.test",
role : "user",
active : true,
createdAt : now()
}
}

}
21 changes: 21 additions & 0 deletions tests/perf-harness/app/modules_app/perf-module/ModuleConfig.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Performance test module — exercises HMVC module routing and module-scoped DI.
*/
component {

this.title = "Perf Module"
this.author = "Ortus Solutions"
this.description = "HMVC module for ColdBox performance testing"
this.version = "1.0.0"
this.entrypoint = "perf-module"
this.modelNamespace = "perf-module"
this.autoMapModels = true

function configure(){
routes = [
{ pattern : "/items", handler : "Items", action : "index" },
{ pattern : "/", handler : "Items", action : "index" }
]
}

}
20 changes: 20 additions & 0 deletions tests/perf-harness/app/modules_app/perf-module/handlers/Items.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Module handler — returns item list as JSON.
*/
component extends="coldbox.system.EventHandler" {

property name="itemService" inject="ItemService@perf-module";

function index( event, rc, prc ){
event.renderData(
type = "json",
data = {
status : "success",
module : "perf-module",
count : 10,
items : itemService.getItems( 10 )
}
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Module-scoped item service singleton.
*/
component singleton {

function getItems( numeric count=10 ){
var result = []
for( var i = 1; i <= arguments.count; i++ ){
result.append({
id : i,
name : "Item #i#",
code : "ITEM-#i#",
value : i * 1.5,
active : true
})
}
return result
}

}
1 change: 1 addition & 0 deletions tests/perf-harness/app/views/api/list.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<cfoutput>#serializeJSON( prc.data ?: {} )#</cfoutput>
21 changes: 21 additions & 0 deletions tests/perf-harness/app/views/main/complex.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<cfoutput>
<div class="perf-complex">
<section class="users">
<h3>Users (#prc.users.len()#)</h3>
<ul>
<cfloop array="#prc.users#" item="u">
<li>#u.id# — #u.firstName# #u.lastName# &lt;#u.email#&gt; [#u.role#]</li>
</cfloop>
</ul>
</section>

<section class="products">
<h3>Products (#prc.products.len()#)</h3>
<ul>
<cfloop array="#prc.products#" item="p">
<li>#p.id# — #p.name# (#p.sku#) $#numberFormat( p.price, "9.99" )# — #p.category#</li>
</cfloop>
</ul>
</section>
</div>
</cfoutput>
10 changes: 10 additions & 0 deletions tests/perf-harness/app/views/main/index.cfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<cfoutput>
<div class="perf-result">
<h2>#prc.message#</h2>
<p>Rendered at: #dateTimeFormat( prc.timestamp, "yyyy-mm-dd HH:nn:ss" )#</p>
<p>ColdBox: #prc.version#</p>
<cfif prc.keyExists( "_perfElapsed" )>
<p>Request elapsed: #prc._perfElapsed#ms</p>
</cfif>
</div>
</cfoutput>
Loading
Loading