directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Directory Server v1.5: Core Integration Testing Framework (page edited)
Date Thu, 05 Jun 2008 11:40:00 GMT
<html>
<head>
    <base href="http://cwiki.apache.org/confluence" />
    <style type="text/css">
    <!--
    body, p, td, table, tr, .bodytext, .stepfield {
	font-family: Verdana, arial, sans-serif;
	font-size: 11px;
	line-height: 16px;
	color: #000000;
	font-weight: normal;
}
#PageContent {
	text-align: left;
	background-color: #fff;
	padding: 0px;
	margin: 0px;
    padding-bottom:20px;
}
/*
** when this stylesheet is used for the Tiny MCE Wysiwyg editor's edit area, we can't
** use an id=PageContent or class=wiki-content, so we must
** set the body style to that used for PageContent, and p to that used for wiki-content.
*/

body {
	margin: 0px;
	padding: 0px;
	text-align: center;
    background-color: #f0f0f0;
}

@media print {

body {
    background-color: #fff;
}

}

.monospaceInput {
    font:12px monospace
}

.wiki-content p, .commentblock p {
    margin: 16px 0px 16px 0px;
    padding: 0px;
}

.wiki-content-preview {
    padding: 5px;
    border-left: 1px solid #3c78b5;
    border-right: 1px solid #3c78b5;
}

ul, ol {
    margin-top: 2px;
    margin-bottom: 2px;
    padding-top: 0px;
    padding-bottom: 0px;
}

pre {
    padding: 0px;
    margin-top: 5px;
    margin-left: 15px;
    margin-bottom: 5px;
    margin-right: 5px;
    text-align: left;
}

.helpheading {
    font-weight: bold;
    background-color: #D0D9BD;
        border-bottom: 1px solid #3c78b5;
        padding: 4px 4px 4px 4px;
        margin: 0px;
        margin-top: 10px;
}
.helpcontent {
        padding: 4px 4px 20px 4px;
    background-color: #f5f7f1;
}

.code {
 	border: 1px dashed #3c78b5;
    font-size: 11px;
	font-family: Courier;
    margin: 10px;
	line-height: 13px;
}

.focusedComment {
    background: #ffffce;
}

.commentBox, .focusedComment {
    padding: 10px;
    margin: 5px 0 5px 0;
    border: 1px #bbb solid;
}

.codeHeader {
    background-color: #f0f0f0;
 	border-bottom: 1px dashed #3c78b5;
    padding: 3px;
	text-align: center;
}

.codeContent {
    text-align: left;
    background-color: #f0f0f0;
    padding: 3px;
}

.preformatted {
 	border: 1px dashed #3c78b5;
    font-size: 11px;
	font-family: Courier;
    margin: 10px;
	line-height: 13px;
}

.preformattedHeader {
    background-color: #f0f0f0;
 	border-bottom: 1px dashed #3c78b5;
    padding: 3px;
	text-align: center;
}

.preformattedContent {
    background-color: #f0f0f0;
    padding: 3px;
}

.panel {
 	border: 1px dashed #3c78b5;
    margin: 10px;
    margin-top: 0px;
}

.panelHeader {
    background-color: #f0f0f0;
 	border-bottom: 1px dashed #3c78b5;
    padding: 3px;
	text-align: center;
}

.panelContent {
    background-color: #f0f0f0;
    padding: 5px;
}

.anonymousAlert {
    background-color: #f0f0f0;
 	border: 1px dashed red;
    font-size: 11px;
    padding: 10px 5px 10px 5px;
    margin: 4px;
	line-height: 13px;
}

.lockAlert {
    background-color: #f0f0f0;
    width: 50%;
 	border: 1px dashed red;
    font-size: 11px;
    padding: 10px 5px 10px 5px;
    margin: 4px;
	line-height: 13px;
}


.code-keyword {
  color: #000091;
  background-color: inherit;
}

.code-object {
  color: #910091;
  background-color: inherit;
}

.code-quote {
  color: #009100;
  background-color: inherit;
}

.code-comment {
  color: #808080;
  background-color: inherit;
}


.code-xml .code-keyword {
  color: inherit;
  font-weight: bold;
}

.code-tag {
  color: #000091;
  background-color: inherit;
}

.breadcrumbs {
    background-color: #f0f0f0;
 	border-color: #3c78b5;
	border-width: 1px 0px 1px 0px;
	border-style: solid;
    font-size: 11px;
    padding: 3px 0px 3px 0px;
}

.navmenu {
    border: 1px solid #ccc;
}

.menuheading {
    font-weight: bold;
    background-color: #f0f0f0;
 	border-bottom: 1px solid #3c78b5;
	padding: 4px 4px 2px 4px;
}

.menuitems {
	padding: 4px 4px 20px 4px;
}

.rightpanel {
    border-left: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
}

#helpheading {
    text-align: left;
    font-weight: bold;
    background-color: #D0D9BD;
 	border-bottom: 1px solid #3c78b5;
	padding: 4px 4px 4px 4px;
	margin: 0px;
}
#helpcontent {
	padding: 4px 4px 4px 4px;
    background-color: #f5f7f1;
}
.helptab-unselected {
    font-weight: bold;
	padding: 5px;
    background-color: #f5f7f1;
}
.helptab-selected {
    font-weight: bold;
    background-color: #D0D9BD;
	padding: 5px;
}
.helptabs {
    margin: 0px;
    background-color: #f5f7f1;
	padding: 5px;
}
.infopanel-heading {
    font-weight: bold;
	padding: 4px 0px 2px 0px;
}

.pagebody {
}

.pageheader {
	padding: 5px 5px 5px 0px;
 	border-bottom: 1px solid #3c78b5;
}

.pagetitle {
	font-size: 22px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: #003366;
}

.newpagetitle {
    color: #ccc !important;
}

.steptitle {
	font-size: 18px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: #003366;
	margin-bottom: 7px;
}

.substeptitle {
    font-size: 12px;
    font-weight: bold;
    font-family: Arial, sans-serif;
    color: #003366;
    margin: 2px 4px 4px 4px;
    padding: 2px 4px 1px 4px;
}

.stepdesc {
    font-family: Verdana, arial, sans-serif;
	font-size: 11px;
	line-height: 16px;
	font-weight: normal;
    color: #666666;
    margin-top: 7px;
    margin-bottom: 7px;
}

.steplabel {
    font-weight: bold;
    margin-right: 4px;
    color: black;
    float: left;
    width: 15%;
    text-align: right;
}

.stepfield {
    background: #f0f0f0;
    padding: 5px;
}

.submitButtons{
    margin-top:5px;
    text-align:right;
}

.formtitle {
	font-size: 12px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: #003366;
}

.sectionbottom {
    border-bottom: 1px solid #3c78b5;
}

.topRow {
    border-top: 2px solid #3c78b5;
}

.tabletitle {
	font-size: 14px;
	font-weight: bold;
	font-family: Arial, sans-serif;
    padding: 3px 0px 2px 0px;
    margin: 8px 4px 2px 0px;
	color: #003366;
	border-bottom: 2px solid #3c78b5;
}
.pagesubheading {
    color: #666666;
    font-size: 10px;
    padding: 0px 0px 5px 0px;
}

HR {
	color: 3c78b5;
	height: 1;
}

A:link, A:visited, A:active, A:hover {
	color: #003366;
}

h1 A:link, h1 A:visited, h1 A:active {
	text-decoration: none;
}

h1 A:hover {
    border-bottom: 1px dotted #003366;
}

.wiki-content > :first-child, .commentblock > :first-child {
    margin-top: 3px;
}

.logocell {
    padding: 10px;
}

input {
	font-family: verdana, geneva, arial, sans-serif;
	font-size: 11px;
	color: #000000;
}

textarea, textarea.editor {
	font-family: verdana, geneva, arial, sans-serif;
	font-size: 11px;
	color: #333333;
}

/* use logoSpaceLink instead.
.spacenametitle {
	font: 21px/31px Impact, Arial, Helvetica;
    font-weight: 100;
    color: #999999;
	margin: 0px;
}
.spacenametitle img {
  margin: 0 0 -4px 0;
}
.spacenametitle a {
    text-decoration: none;
    color: #999999;
}
.spacenametitle a:visited {
    text-decoration: none;
    color: #999999;
}*/

.spacenametitle-printable {
	font: 20px/25px Impact, Arial, Helvetica;
    font-weight: 100;
    color: #999999;
	margin: 0px;
}
.spacenametitle-printable a {
    text-decoration: none;
    color: #999999;
}
.spacenametitle-printable a:visited {
    text-decoration: none;
    color: #999999;
}

.blogDate {
	font-weight: bold;
	text-decoration: none;
	color: black;
}

.blogSurtitle {
    background: #f0f0f0;
 	border: 1px solid #ddd;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.blogHeading {
    font-size: 20px;
    line-height: normal;
    font-weight: bold;
    padding: 0px;
    margin: 0px;
}

.blogHeading a {
   text-decoration: none;
   color: black;
}

.endsection {
	align: right;
	color: #666666;
	margin-top: 10px;
}
.endsectionleftnav {
	align: right;
	color: #666666;
	margin-top: 10px;
}

h1 {
	font-size: 24px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	color: #003366;
 	border-bottom: 1px solid #3c78b5;
	padding: 2px;
	margin: 36px 0px 4px 0px;
}

h2 {
	font-size: 18px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
 	border-bottom: 1px solid #3c78b5;
	padding: 2px;
	margin: 27px 0px 4px 0px;
}

h3 {
	font-size: 14px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 21px 0px 4px 0px;
}

h4 {
	font-size: 12px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 18px 0px 4px 0px;
}

h4.search {
	font-size: 12px;
	line-height: normal;
	font-weight: normal;
	background-color: #f0f0f0;
	padding: 4px;
	margin: 18px 0px 4px 0px;
}

h5 {
	font-size: 10px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 14px 0px 4px 0px;
}

h6 {
	font-size: 8px;
	line-height: normal;
	font-weight: bold;
	background-color: #f0f0f0;
	padding: 2px;
	margin: 14px 0px 4px 0px;
}

.smallfont {
    font-size: 10px;
}
.descfont {
    font-size: 10px;
    color: #666666;
}
.smallerfont {
    font-size: 9px;
}
.smalltext {
    color: #666666;
    font-size: 10px;
}
.smalltext a {
    color: #666666;
}
.smalltext-blue {
    color: #3c78b5;
    font-size: 10px;
}
.surtitle {
    margin-left: 1px;
    margin-bottom: 5px;
    font-size: 14px;
    color: #666666;
}

/* css hack found here:  http://www.fo3nix.pwp.blueyonder.co.uk/tutorials/css/hacks/ */
.navItemOver { font-size: 10px; font-weight: bold; color: #ffffff; background-color: #003366; cursor: hand; voice-family: '\'}\''; voice-family:inherit; cursor: pointer;}
.navItemOver a { color: #ffffff; background-color:#003366; text-decoration: none; }
.navItemOver a:visited { color: #ffffff; background-color:#003366; text-decoration: none; }
.navItemOver a:hover { color: #ffffff; background-color:#003366; text-decoration: none; }
.navItem { font-size: 10px; font-weight: bold; color: #ffffff; background-color: #3c78b5; }
.navItem a { color: #ffffff; text-decoration: none; }
.navItem a:hover { color: #ffffff; text-decoration: none; }
.navItem a:visited { color: #ffffff; text-decoration: none; }

div.padded { padding: 4px; }
div.thickPadded { padding: 10px; }
h3.macrolibrariestitle {
    margin: 0px 0px 0px 0px;
}

div.centered { text-align: center; margin: 10px; }
div.centered table {margin: 0px auto; text-align: left; }

.tableview table {
    margin: 0;
}

.tableview th {
    text-align: left;
    color: #003366;
    font-size: 12px;
    padding: 5px 0px 0px 5px;
    border-bottom: 2px solid #3c78b5;
}
.tableview td {
    text-align: left;
    border-color: #ccc;
    border-width: 0px 0px 1px 0px;
    border-style: solid;
    margin: 0;
    padding: 4px 10px 4px 5px;
}

.grid {
    margin: 2px 0px 5px 0px;
    border-collapse: collapse;
}
.grid th  {
    border: 1px solid #ccc;
    padding: 2px 4px 2px 4px;
    background: #f0f0f0;
    text-align: center;
}
.grid td  {
    border: 1px solid #ccc;
    padding: 3px 4px 3px 4px;
}
.gridHover {
	background-color: #f9f9f9;
}

td.infocell {
    background-color: #f0f0f0;
}
.label {
	font-weight: bold;
	color: #003366;
}

label {
	font-weight: bold;
	color: #003366;
}

.error {
	background-color: #fcc;
}

.errorBox {
	background-color: #fcc;
    border: 1px solid #c00;
    padding: 5px;
    margin: 5px;
}

.errorMessage {
	color: #c00;
}

.success {
	background-color: #dfd;
}

.successBox {
	background-color: #dfd;
    border: 1px solid #090;
    padding: 5px;
    margin-top:5px;
    margin-bottom:5px;
}

blockquote {
	padding-left: 10px;
	padding-right: 10px;
	margin-left: 5px;
	margin-right: 0px;
	border-left: 1px solid #3c78b5;
}

table.confluenceTable
{
    margin: 5px;
    border-collapse: collapse;
}

/* Added as a temporary fix for CONF-4223. The table elements appear to be inheriting the border: none attribute from the sectionMacro class */
table.confluenceTable td.confluenceTd
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
}

/* Added as a temporary fix for CONF-4223. The table elements appear to be inheriting the border: none attribute from the sectionMacro class */
table.confluenceTable th.confluenceTh
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
    background-color: #f0f0f0;
    text-align: center;
}

td.confluenceTd
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
}

th.confluenceTh
{
    border-width: 1px;
    border-style: solid;
    border-color: #ccc;
    padding: 3px 4px 3px 4px;
    background-color: #f0f0f0;
    text-align: center;
}

DIV.small {
	font-size: 9px;
}

H1.pagename {
	margin-top: 0px;
}

IMG.inline  {}

.loginform {
    margin: 5px;
    border: 1px solid #ccc;
}

/* The text how the "This is a preview" comment should be shown. */
.previewnote { text-align: center;
                font-size: 11px;
                    color: red; }

/* How the preview content should be shown */
.previewcontent { background: #E0E0E0; }

/* How the system messages should be shown (DisplayMessage.jsp) */
.messagecontent { background: #E0E0E0; }

/* How the "This page has been modified..." -comment should be shown. */
.conflictnote { }

.createlink {
    color: maroon;
}
a.createlink {
    color: maroon;
}
.templateparameter {
    font-size: 9px;
    color: darkblue;
}

.diffadded {
    background: #ddffdd;
    padding: 1px 1px 1px 4px;
	border-left: 4px solid darkgreen;
}
.diffdeleted {
    color: #999;
    background: #ffdddd;
    padding: 1px 1px 1px 4px;
	border-left: 4px solid darkred;
}
.diffnochange {
    padding: 1px 1px 1px 4px;
	border-left: 4px solid lightgrey;
}
.differror {
    background: brown;
}
.diff {
    font-family: lucida console, courier new, fixed-width;
	font-size: 12px;
	line-height: 14px;
}
.diffaddedchars {
    background-color:#99ff99;
    font-weight:bolder;
}
.diffremovedchars {
    background-color:#ff9999;
    text-decoration: line-through;
    font-weight:bolder;
}

.greybackground {
    background: #f0f0f0
}

.greybox {
 	border: 1px solid #ddd;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.borderedGreyBox {
    border: 1px solid #cccccc;
    background-color: #f0f0f0;
    padding: 10px;
}

.greyboxfilled {
 	border: 1px solid #ddd;
    background: #f0f0f0;
    padding: 3px;
	margin: 1px 1px 10px 1px;
}

.navBackgroundBox {
    padding: 5px 5px 5px 5px;
    font-size: 22px;
	font-weight: bold;
	font-family: Arial, sans-serif;
	color: white;
    background: #3c78b5;
    text-decoration: none;
}

.previewBoxTop {
	background-color: #f0f0f0;
    border-width: 1px 1px 0px 1px;
    border-style: solid;
    border-color: #3c78b5;
    padding: 5px;
    margin: 5px 0px 0px 0px;
    text-align: center;
}
.previewContent {
    background-color: #fff;
 	border-color: #3c78b5;
	border-width: 0px 1px 0px 1px;
	border-style: solid;
	padding: 10px;
	margin: 0px;
}
.previewBoxBottom {
	background-color: #f0f0f0;
    border-width: 0px 1px 1px 1px;
    border-style: solid;
    border-color: #3c78b5;
    padding: 5px;
    margin: 0px 0px 5px 0px;
    text-align: center;
}

.functionbox {
    background-color: #f0f0f0;
 	border: 1px solid #3c78b5;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.functionbox-greyborder {
    background-color: #f0f0f0;
 	border: 1px solid #ddd;
	padding: 3px;
	margin: 1px 1px 10px 1px;
}

.search-highlight {
    background-color: #ffffcc;
}

/* normal (white) background */
.rowNormal {
    background-color: #ffffff;
 }

/* alternate (pale yellow) background */
.rowAlternate {
    background-color: #f7f7f7;
}

/* used in the list attachments table */
.rowAlternateNoBottomColor {
    background-color: #f7f7f7;
}

.rowAlternateNoBottomNoColor {
}

.rowAlternateNoBottomColor td {
    border-bottom: 0px;
}

.rowAlternateNoBottomNoColor td {
    border-bottom: 0px;
}

/* row highlight (grey) background */
.rowHighlight {
    background-color: #f0f0f0;

}

TD.greenbar {FONT-SIZE: 2px; BACKGROUND: #00df00; BORDER: 1px solid #9c9c9c; PADDING: 0px; }
TD.redbar {FONT-SIZE: 2px; BACKGROUND: #df0000; BORDER: 1px solid #9c9c9c; PADDING: 0px; }
TD.darkredbar {FONT-SIZE: 2px; BACKGROUND: #af0000; BORDER: 1px solid #9c9c9c; PADDING: 0px; }

TR.testpassed {FONT-SIZE: 2px; BACKGROUND: #ddffdd; PADDING: 0px; }
TR.testfailed {FONT-SIZE: 2px; BACKGROUND: #ffdddd; PADDING: 0px; }

.toolbar  {
    margin: 0px;
    border-collapse: collapse;
}

.toolbar td  {
    border: 1px solid #ccc;
    padding: 2px 2px 2px 2px;
    color: #ccc;
}

td.noformatting {
    border-width: 0px;
    border-style: none;
    text-align: center;
	padding: 0px;
}

.commentblock {
    margin: 12px 0 12px 0;
}

/*
 * Divs displaying the license information, if necessary.
 */
.license-eval, .license-none, .license-nonprofit {
    border-top: 1px solid #bbbbbb;
    text-align: center;
    font-size: 10px;
    font-family: Verdana, Arial, Helvetica, sans-serif;
}

.license-eval, .license-none {
    background-color: #ffcccc;
}

.license-eval b, .license-none b {
    color: #990000
}

.license-nonprofit {
    background-color: #ffffff;
}

/*
 * The shadow at the bottom of the page between the main content and the
 * "powered by" section.
 */
.bottomshadow {
    height: 12px;
    background-image: url("$req.contextPath/images/border/border_bottom.gif");
    background-repeat: repeat-x;
}

/*
 * Styling of the operations box
 */
.navmenu .operations li, .navmenu .operations ul {
    list-style: none;
    margin-left: 0;
    padding-left: 0;
}

.navmenu .operations ul {
    margin-bottom: 9px;
}

.navmenu .label {
    font-weight: inherit;
}

/*
 * Styling of ops as a toolbar
 */
.toolbar div {
    display: none;
}

.toolbar .label {
    display: none;
}

.toolbar .operations {
    display: block;
}

.toolbar .operations ul {
    display: inline;
    list-style: none;
    margin-left: 10px;
    padding-left: 0;
}

.toolbar .operations li {
    list-style: none;
    display: inline;
}

/* list page navigational tabs */
#foldertab {
padding: 3px 0px 3px 8px;
margin-left: 0;
border-bottom: 1px solid #3c78b5;
font: bold 11px Verdana, sans-serif;
}

#foldertab li {
list-style: none;
margin: 0;
display: inline;
}

#foldertab li a {
padding: 3px 0.5em;
margin-left: 3px;
border: 1px solid #3c78b5;
border-bottom: none;
background: #3c78b5;
text-decoration: none;
}

#foldertab li a:link { color: #ffffff; }
#foldertab li a:visited { color: #ffffff; }

#foldertab li a:hover {
color: #ffffff;
background: #003366;
border-color: #003366;
}

#foldertab li a.current {
background: white;
border-bottom: 1px solid white;
color: black;
}

#foldertab li a.current:link { color: black; }
#foldertab li a.current:visited { color: black; }
#foldertab li a.current:hover {
background: white;
border-bottom: 1px solid white;
color: black;
}

/* alphabet list */
ul#squaretab {
margin-left: 0;
padding-left: 0;
white-space: nowrap;
font: bold 8px Verdana, sans-serif;
}

#squaretab li {
display: inline;
list-style-type: none;
}

#squaretab a {
padding: 2px 6px;
border: 1px solid #3c78b5;
}

#squaretab a:link, #squaretab a:visited {
color: #fff;
background-color: #3c78b5;
text-decoration: none;
}

#squaretab a:hover {
color: #ffffff;
background-color: #003366;
border-color: #003366;
text-decoration: none;
}

#squaretab li a#current {
background: white;
color: black;
}

.blogcalendar * {
    font-family:verdana, arial, sans-serif;
    font-size:x-small;
    font-weight:normal;
    line-height:140%;
    padding:2px;
}


table.blogcalendar {
    border: 1px solid #3c78b5;
}

.blogcalendar th.calendarhead, a.calendarhead {
    font-size:x-small;
    font-weight:bold;
    padding:2px;
    text-transform:uppercase;
    background-color: #3c78b5;
    color: #ffffff;
    letter-spacing: .3em;
    text-transform: uppercase;
}

.calendarhead:visited {color: white;}
.calendarhead:active {color: white;}
.calendarhead:hover {color: white;}

.blogcalendar th {
    font-size:x-small;
    font-weight:bold;
    padding:2px;
    background-color:#f0f0f0;
}

.blogcalendar td {
    font-size:x-small;
    font-weight:normal;
}

.searchGroup { padding: 0 0 10px 0; background: #f0f0f0; }
.searchGroupHeading { font-size: 10px; font-weight: bold; color: #ffffff; background-color: #3c78b5; padding: 2px 4px 1px 4px; }
.searchItem { padding: 1px 4px 1px 4px; }
.searchItemSelected { padding: 1px 4px 1px 4px; font-weight: bold; background: #ddd; }

/* permissions page styles */
.permissionHeading {
    border-bottom: #bbb; border-width: 0 0 1px 0; border-style: solid; font-size: 16px; text-align: left;
}
.permissionTab {
    border-width: 0 0 0 1px; border-style: solid; background: #3c78b5; color: #ffffff; font-size: 10px;
}
.permissionSuperTab {
    border-width: 0 0 0 1px; border-style: solid; background: #003366; color: #ffffff;
}
.permissionCell {
    border-left: #bbb; border-width: 0 0 0 1px; border-style: solid;
}

/* warning panel */
.warningPanel { background: #FFFFCE; border:#F0C000 1px solid; padding: 8px; margin: 10px; }
/* alert panel */
.alertPanel { background: #FFCCCC; border:#C00 1px solid; padding: 8px; margin: 10px; }
/* info panel */
.infoPanel { background: #D8E4F1; border:#3c78b5 1px solid; padding: 8px; margin: 10px; }

/* side menu highlighting (e.g. space content screen) */
.optionPadded { padding: 2px; }
.optionSelected { background-color: #ffffcc; padding: 2px; border: 1px solid #ddd; margin: -1px; }
.optionSelected a { font-weight: bold; text-decoration: none; color: black; }

/* information macros */
.noteMacro { border-style: solid; border-width: 1px; border-color: #F0C000; background-color: #FFFFCE; text-align:left; margin-top: 5px; margin-bottom: 5px}
.warningMacro { border-style: solid; border-width: 1px; border-color: #c00; background-color: #fcc; text-align:left; margin-top: 5px; margin-bottom: 5px}
.infoMacro { border-style: solid; border-width: 1px; border-color: #3c78b5; background-color: #D8E4F1; text-align:left; margin-top: 5px; margin-bottom: 5px}
.tipMacro { border-style: solid; border-width: 1px; border-color: #090; background-color: #dfd; text-align:left; margin-top: 5px; margin-bottom: 5px}
.informationMacroPadding { padding: 5px 0 0 5px; }

table.infoMacro td, table.warningMacro td, table.tipMacro td, table.noteMacro td, table.sectionMacro td {
    border: none;
}

table.sectionMacroWithBorder td.columnMacro { border-style: dashed; border-width: 1px; border-color: #cccccc;}

.pagecontent
{
    padding: 10px;
    text-align: left;
}

/* styles for links in the top bar */
.topBarDiv a:link {color: #ffffff;}
.topBarDiv a:visited {color: #ffffff;}
.topBarDiv a:active {color: #ffffff;}
.topBarDiv a:hover {color: #ffffff;}
.topBarDiv {color: #ffffff;}

.topBar {
    background-color: #003366;
}


/* styles for extended operations */
.greyLinks a:link {color: #666666; text-decoration:underline;}
.greyLinks a:visited {color: #666666; text-decoration:underline;}
.greyLinks a:active {color: #666666; text-decoration:underline;}
.greyLinks a:hover {color: #666666; text-decoration:underline;}
.greyLinks {color: #666666; display:block; padding: 10px}

.logoSpaceLink {color: #999999; text-decoration: none}
.logoSpaceLink a:link {color: #999999; text-decoration: none}
.logoSpaceLink a:visited {color: #999999; text-decoration: none}
.logoSpaceLink a:active {color: #999999; text-decoration: none}
.logoSpaceLink a:hover {color: #003366; text-decoration: none}

/* basic panel (basicpanel.vmd) style */
.basicPanelContainer {border: 1px solid #3c78b5; margin-top: 2px; margin-bottom: 8px; width: 100%}
.basicPanelTitle {padding: 5px; margin: 0px; background-color: #f0f0f0; color: black; font-weight: bold;}
.basicPanelBody {padding: 5px; margin: 0px}

.separatorLinks a:link {color: white}
.separatorLinks a:visited {color: white}
.separatorLinks a:active {color: white}

.greynavbar {background-color: #f0f0f0; border-top: 1px solid #3c78b5; margin-top: 2px}

div.headerField {
    float: left;
    width: auto;
    height: 100%;
}

.headerFloat {
    margin-left: auto;
    width: 50%;
}

.headerFloatLeft {
    float: left;
    margin-right: 20px;
    margin-bottom: 10px;
}

#headerRow {
    padding: 10px;
}

div.license-personal {
   background-color: #003366;
   color: #ffffff;
}

div.license-personal a {
   color: #ffffff;
}

.greyFormBox {
    border: 1px solid #cccccc;
    padding: 5px;
}

/* IE automatically adds a margin before and after form tags. Use this style to remove that */
.marginlessForm {
    margin: 0px;
}

.openPageHighlight {
    background-color: #ffffcc;
    padding: 2px;
    border: 1px solid #ddd;
}

.editPageInsertLinks, .editPageInsertLinks a
{
    color: #666666;
    font-weight: bold;
    font-size: 10px;
}

/* Style for label heatmap. */
.top10 a {
    font-weight: bold;
    font-size: 2em;
    color: #003366;
}
.top25 a {
    font-weight: bold;
    font-size: 1.6em;
    color: #003366;
}
.top50 a {
    font-size: 1.4em;
    color: #003366;
}
.top100 a {
    font-size: 1.2em;
    color: #003366;
}

.heatmap {
    list-style:none;
    width: 95%;
    margin: 0px auto;
}

.heatmap a {
    text-decoration:none;
}

.heatmap a:hover {
    text-decoration:underline;
}

.heatmap li {
    display: inline;
}

.minitab {
padding: 3px 0px 3px 8px;
margin-left: 0;
margin-top: 1px;
margin-bottom: 0px;
border-bottom: 1px solid #3c78b5;
font: bold 9px Verdana, sans-serif;
text-decoration: none;
float:none;
}
.selectedminitab {
padding: 3px 0.5em;
margin-left: 3px;
margin-top: 1px;
border: 1px solid #3c78b5;
background: white;
border-bottom: 1px solid white;
color: #000000;
text-decoration: none;
}
.unselectedminitab {
padding: 3px 0.5em;
margin-left: 3px;
margin-top: 1px;
border: 1px solid #3c78b5;
border-bottom: none;
background: #3c78b5;
color: #ffffff;
text-decoration: none;
}

a.unselectedminitab:hover {
color: #ffffff;
background: #003366;
border-color: #003366;
}

a.unselectedminitab:link { color: white; }
a.unselectedminitab:visited { color: white; }

a.selectedminitab:link { color: black; }
a.selectedminitab:visited { color: black; }

.linkerror { background-color: #fcc;}

a.labelOperationLink:link {text-decoration: underline}
a.labelOperationLink:active {text-decoration: underline}
a.labelOperationLink:visited {text-decoration: underline}
a.labelOperationLink:hover {text-decoration: underline}

a.newLabel:link {background-color: #ddffdd}
a.newLabel:active {background-color: #ddffdd}
a.newLabel:visited {background-color: #ddffdd}
a.newLabel:hover {background-color: #ddffdd}

ul.square {list-style-type: square}

.inline-control-link {
    background: #ffc;
    font-size: 9px;
    color: #666;
    padding: 2px;
    text-transform: uppercase;
    text-decoration: none;
}


.inline-control-link a:link {text-decoration: none}
.inline-control-link a:active {text-decoration: none}
.inline-control-link a:visited {text-decoration: none}
.inline-control-link a:hover {text-decoration: none}

.inline-control-link {
    background: #ffc;
    font-size: 9px;
    color: #666;
    padding: 2px;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
}

div.auto_complete {
    width: 350px;
    background: #fff;
}
div.auto_complete ul {
    border: 1px solid #888;
    margin: 0;
    padding: 0;
    width: 100%;
    list-style-type: none;
}
div.auto_complete ul li {
    margin: 0;
    padding: 3px;
}
div.auto_complete ul li.selected {
    background-color: #ffb;
}
div.auto_complete ul strong.highlight {
    color: #800;
    margin: 0;
    padding: 0;
}

/******* Edit Page Styles *******/
.toogleFormDiv{
    border:1px solid #A7A6AA;
    background-color:white;
    padding:5px;
    margin-top: 5px;
}

.toogleInfoDiv{
    border:1px solid #A7A6AA;
    background-color:white;
    display:none;
    padding:5px;
    margin-top: 10px;
}

.inputSection{
    margin-bottom:20px;
}

#editBox{
   border:1px solid lightgray;
   background-color:#F0F0F0;
}

/******* Left Navigation Theme Styles ********/
.leftnav li a {
    text-decoration:none;
    color:white;
    margin:0px;
    display:block;
    padding:2px;
    padding-left:5px;
    background-color: #3c78b5;
    border-top:1px solid #3c78b5;
}

.leftnav li a:active {color:white;}
.leftnav li a:visited {color:white;}
.leftnav li a:hover {background-color: #003366; color:white;}

/* Added by Shaun during i18n */
.replaced
{
    background-color: #33CC66;
}

.topPadding
{
    margin-top: 20px;
}

/* new form style */
.form-block {
    padding: 6px;
}
.form-error-block {
    padding: 6px;
    background: #fcc;
    border-top: #f0f0f0 1px solid;
    border-bottom: #f0f0f0 1px solid;
    margin-bottom: 6px;
    padding: 0 12px 0 12px;
}
.form-element-large {
    font-size: 16px;
    font-weight: bold;
    font-family: Arial, sans-serif;
    color: #003366;
}

.form-element-small {
    font-size: 12px;
    font-weight: bold;
    font-family: Arial, sans-serif;
    color: #003366;
}

.form-header {
    background: lightyellow;
    border-top: #f0f0f0 1px solid;
    border-bottom: #f0f0f0 1px solid;
    margin-bottom: 6px;
    padding: 0 12px 0 12px;
}
.form-header p, .form-block p, .form-error-block p {
    line-height: normal;
    margin: 12px 0 12px 0;
}
.form-example {
    color: #888;
    font-size: 11px;
}
.form-divider {
    border-bottom: #ccc 1px solid;
    margin-bottom: 6px;
}
.form-buttons {
    margin-top: 6px;
    border-top: #ccc 1px solid;
    border-bottom: #ccc 1px solid;
    background: #f0f0f0;
    padding: 10px;
    text-align: center;
}
.form-buttons input {
    width: 100px;
}
.form-block .error {
    padding: 6px;
    margin-bottom: 6px;
}
    -->
    </style>
</head>
<body>

<div id="PageContent">
<table class="pagecontent" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>
<td valign="top" class="pagebody">

    <div class="pageheader">
        <span class="pagetitle">
            Page Edited :
            <a href="http://cwiki.apache.org/confluence/display/DIRxSRVx11">DIRxSRVx11</a> :
            <a href="http://cwiki.apache.org/confluence/display/DIRxSRVx11/Core+Integration+Testing+Framework">Core Integration Testing Framework</a>
        </span>
    </div>

     <p>
        <a href="http://cwiki.apache.org/confluence/display/DIRxSRVx11/Core+Integration+Testing+Framework">Core Integration Testing Framework</a>
        has been edited by             <a href="http://cwiki.apache.org/confluence/display/~elecharny">Emmanuel Lécharny</a>
            <span class="smallfont">(Jun 05, 2008)</span>.
     </p>
    
     <p>
                 <a href="http://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=71929&originalVersion=29&revisedVersion=30">(View changes)</a>
     </p>

    <span class="label">Content:</span><br/>
    <div class="greybox wiki-content"><table cellpadding='5' width='85%' cellspacing='8px' class='noteMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/warning.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>
<p>Work in progress.</p></td></tr></table>

<h1><a name="CoreIntegrationTestingFramework-Introduction"></a>Introduction</h1>

<p>In the 1.5 branch, we upgraded to JUnit 4.4 from JUnit 3.8.1.  This new version of JUnit is architecturally different from the old version used and it offers several advantages which we can exploit.  These JUnit 4.4 advances in combination with Java 5 annotations and the new snapshotting feature resulted in some very interesting ideas for a new integration testi framework.</p>
<table cellpadding='5' width='85%' cellspacing='8px' class='tipMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/check.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>
<p>The material below presumes you already have a working knowledge of JUnit 4.4 as well as the older 3.8.1 version. If you need a primer there are several out there however we have one <span class="nobr"><a href="http://cwiki.apache.org/confluence/display/DIRxDEV/JUnit4+primer" title="Visit page outside Confluence" rel="nofollow">here<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> as well.</p></td></tr></table>

<h2><a name="CoreIntegrationTestingFramework-Whatareintegrationtests%3F"></a>What are integration tests?</h2>

<p>Let's quickly define what an LDAP or Apache DS based integration test may mean to you:</p>

<p>&#35;1 You have an LDAP driven application that requires a live LDAP server (or what feels like one) to be present and available to test your application as realistic a simulated environment as possible.<br/>
&#35;2 You're an Apache Directory Developer or user and you want to quickly setup many test cases where the server is running and you can test various scenarios.</p>
<ul>
	<li>
	<ul>
		<li>Users who want rapid resolutions can submit test cases</li>
		<li>Developers - well do we have to say why we need em?
<table cellpadding='5' width='85%' cellspacing='8px' class='infoMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>The core framework simply starts the heart of the directory server without starting various network services.  The core can still be used with the JNDI wrapper to perform tests as if they were occurring over the wire.  The JNDI wrapper around the core mimics the standard JNDI LDAP interface regardless of the fact that calls are not using the network but are directly affecting entries on disk.  Sometimes this might be preferred over network tests.  If you want to perform network based tests then you want to use the server framework which parallels all the concepts here but uses different tags and runners as you can see here <span class="nobr"><a href="/confluence/pages/createpage.action?spaceKey=DIRxSRVx11&amp;title=Server+Integration+Testing+Framework&amp;linkCreation=true&amp;fromPageId=71929" title="Create Page: Server Integration Testing Framework" class="createlink">Server Integration Testing Framework<sup><img class="rendericon" src="/confluence/images/icons/plus.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span>.  Regardless most of the concepts here will also apply to the server framework.</td></tr></table></li>
	</ul>
	</li>
</ul>


<h2><a name="CoreIntegrationTestingFramework-PieIntheSky"></a>Pie In the Sky</h2>

<p>Let's talk the possibilities in terms of "what if you could do .." scenarios.  Just imagine the simplest case where you just want a standard instance of the core directory service up and running so you can just write a bunch of test methods.  It would be real nice to just specify a runner to use and this runner would simply start the directory server and stop it:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">@RunWith( CiRunner.class)
<span class="code-keyword">public</span> class SampleITest
{
    <span class="code-keyword">public</span> <span class="code-keyword">static</span> DirectoryService service;

    @Test <span class="code-keyword">public</span> void checkService0() <span class="code-keyword">throws</span> Exception
    {
        assertNotNull( service );
        assertTrue( service.isStarted() );
    }
}</pre>
</div></div>
<p>All we did was create a simple class (which note does not extend <em>TestCase</em>) with a single test method.  This basic test method, checkService(), is tagged using the JUnit 4 <font color="#990000"><b>@Test</b></font> annotation to declare the method a test case.  This is all thanks to JUnit 4.</p>

<p>Now closer inspection shows that there is no initialization of the static <em>DirectoryService</em> parameter yet this test should pass.  The idea is to have the custom runner specified with the JUnit 4 <font color="#990000"><b>@RunWith</b></font> annotation drive the instantiation, configuration and startup of the core service.  The test method is merely left to conduct it's tests.</p>

<p>This is pretty cool but wait&#33; What happens if we have two tests and one changes the contents of directory? Won't the two tests collide to produce false negatives like in this scenario? :</p>
<div class="code"><div class="codeContent">
<pre class="code-java">@RunWith( CiRunner.class)
<span class="code-keyword">public</span> class SampleITest
{
    <span class="code-keyword">static</span> DirectoryService service;

    @Test <span class="code-keyword">public</span> void testUserAdd() <span class="code-keyword">throws</span> Exception
    {
         addUser( <span class="code-quote">"uid=akarasulu,ou=users,ou=system"</span>, <span class="code-quote">"secret"</span> );
    }


    @Test <span class="code-keyword">public</span> void testAddOuThenModifyAndCheck() <span class="code-keyword">throws</span> Exception
    {
         addUser( <span class="code-quote">"uid=akarasulu,ou=users,ou=system"</span>, <span class="code-quote">"secret"</span> );
         setUserPassword( <span class="code-quote">"uid=akarasulu,ou=users,ou=system"</span>, <span class="code-quote">"new_secret"</span> );
    }
}</pre>
</div></div>
<p>So if the server is started for this test class and these tests run.  The one that runs second will fail because the user entry will have already been created.  We could have written the test to delete the user if that user was present before adding that user, however this changes the characteristics of the test case.</p>

<p>It sure would be nice if the runner could give us the service in it's original state.  And this is what we did before in the core-unit project with the JUnit 3.8.1 based unit tests using setUp() and tearDown() methods in a special AbstractTestCase.  The biggest problem with this approach is the confusing overriding of the configuration by subclasses, and the needless time and CPU this wastes to shutdown, destroy, delete, create, and start new instances of the server.</p>

<p>With the new snapshot capability built into the core we can revert the server to a previous state that was tagged.  The server then appears just like new to tests while the overhead of a shutdown, destroy, delete, create and start are not felt for each test.  A 6-7 time reduction in the time taken for integration tests to run occurred on average using this revert feature.</p>

<p>So to answer the questions above, no the two tests will not collide.  The runner will automatically tag the point at which a test starts.  Then the test is run.  When the next test is run the service is rolled back to it's original state before the server started running the first test.</p>

<p> Sometimes though tests may not need a roll back.  Some may want to accumulate changes, others might want pristine installations with this expensive cycle, some may just need a restart with persistence of data, and others might not even require a service to be running at all.</p>

<h3><a name="CoreIntegrationTestingFramework-Let%27saddaSetupModeannotation%5C%21"></a>Let's add a SetupMode annotation&#33;</h3>

<p>We're in Java 5 land so we can write our own annotation as a cue to the CiRunner.  The runner will use this annotation either on the test class or on the test method to determine how to setup the service.  If the value is set on the test class then all test methods will default to that value.  When present on the test class and on a test method then the test method value is more specific and it overrides the test class SetupMode.  If not specified on either a class or on a test method a system wide default is used: see the tip on defaults below.  Initially we can allow the following enumerated values to the SetupMode annotation:</p>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> Value </th>
<th class='confluenceTh'> Description </th>
</tr>
<tr>
<td class='confluenceTd'> <b>NOSERVICE</b> </td>
<td class='confluenceTd'> Nothing at all is done - a service in not required for tests but if one is running we don't care </td>
</tr>
<tr>
<td class='confluenceTd'> <b>PRISTINE</b> </td>
<td class='confluenceTd'> Stops running service if present, destroys their working directories, creates and configures a new instance of the service then starts it up for every test. </td>
</tr>
<tr>
<td class='confluenceTd'> <b>RESTART</b> </td>
<td class='confluenceTd'> Stops running service if present, then starts it without working directory destruction.  The same service instance is used without reinstantiation or reconfiguration.  If no service is present then one is created and started. </td>
</tr>
<tr>
<td class='confluenceTd'> <b>ROLLBACK</b> </td>
<td class='confluenceTd'> Rolls back the state of a running service live before a test is run. If an old service is present yet no t running it is cleaned up, a new service is created and started.  If no service is present or running then a new service is created and started. </td>
</tr>
<tr>
<td class='confluenceTd'> <b>CUMULATIVE</b> </td>
<td class='confluenceTd'> If a service is running nothing is done.  If a service has been stopped it is started.  If a service is not present one is created and started. </td>
</tr>
</tbody></table>
<p>So now we have the concept of a setup mode with test class level defaults that can be overridden for specific test methods which controls the setup life cycle.  We have a good idea of the most common cases above and can add others if need be.</p>
<table cellpadding='5' width='85%' cellspacing='8px' class='infoMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td><b class="strong">"SetupMode Defaults and Inheritence"</b><br />
<p>A system wide default is needed for the SetupMode.  We chose to use ROLLBACK because of various reasons that are explained in the appendix.  It's nice to override this default with specific test classes and test methods.  Soon with the discussion of suites, we will encounter another level of potentially overriding the SetupMode.  As you may have noticed some kind of SetupMode inheritence is begining to emerge.  Everything inherits from the system wide default.  Test classes may inherit their SetupMode from the test suite level, and test methods may inherit their SetupMode from a test class.  Even though this has nothing to do with Java Class or Annotation inheritance it still is a form of custom property inheritance.  Defaults coupled with inheritance helps reduce the verbosity of annotations.  It can also help in other ways as well.  We may attempt to leverage this for other aspects of the framework but we must be careful because it can cause problems to arise.</p></td></tr></table>

<h3><a name="CoreIntegrationTestingFramework-TheCUMULATIVESetupModemakesnosensewithouttestordering%5C%21"></a>The CUMULATIVE SetupMode makes no sense without test ordering&#33;</h3>

<p>Yeah it makes no sense to accumulate changes.  Why would we bother to do this unless one test needs the results of the other.  Some would say if you need the results of another test just execute that test as a method at the start of your dependent test method.  This works too.  But then there is no reason for having a CUMULATIVE setup mode then though.</p>

<p>We need to figure out just how useful this is to us.  However one option to use in conjunction with the CUMULATIVE SetupMode would be to enable a precondition/precursor/dependency kind of annotation.  This annotation would tell the running to execute certain test methods before others.  It would also have to detect potential cycles but this is not hard.  Then it would establish a test plan and use that to drive the order of test method execution.</p>

<p>I really like this idea in general besides it's usefulness when paired with the CUMULATIVE SetupMode because it takes away that age old hack I used to use to order my tests.  The tests are ordered by JUnit using the getDeclaredMethod() mechanism to query the methods in a class.  This uses regular alphabetical word ordering but this may have odd results especially with internationalization.  It's just a hack and this feature is long overdue in JUnit.  It would be nice to have it here for our framework.  At a minimum it can be used to run simpler tests first.  Also if a simple test fails which another complex test depends on then the depending test can simply be aborted rather than being marked as a failure.</p>

<h2><a name="CoreIntegrationTestingFramework-Whataboutconfigurationandsetup%3F"></a>What about configuration and setup?</h2>

<p>Obviously the pie in the sky picture overlooks some important things.  You want to control how the server is configured and preload it with data if that is a requirement.  These are things that can be handled elegantly using annotations and the power of a custom framework.  Furthermore they can be leveraged for documentation purposes to better describe and document tests as well as what they are doing.</p>

<h3><a name="CoreIntegrationTestingFramework-UsingReusableFactories%28Builders%3F%29"></a>Using Reusable Factories (Builders?)</h3>

<p>I always hated writing setUp and tearDown code in the old JUnit 3.8 way.  You could not control it well enough for the entire class or individual tests.  Then for complex tests you have mix a lot of configuration, startup and data loading all together and it gets messy.  Then with inheritance using a base class things get a little better but it gets a bit confusing with life cycle aspects as you try to have subclasses tweak configuration a bit.</p>

<p>What if we divide and conquer to clearly separate service creation from any preparatory work needed to be done.  Also instead of using inline methods that were not very useful what about using factories (or builder objects) to produce instances of the service.  This is really nice for reuse and furthermore it allows us to utilize containers which essentially serve as builders that wire together services.  Furthermore this can be coupled with the same concepts used before with the SetupMode annotation.  Again an annotation can be used to specify the factory to be used, along with same pattern of defaults and inheritance.</p>

<p>So let's presume a Factory annotation exists which contains a Class.  The system wide default is the default constructor of the DefaultDirectoryService.  A special default factory is created for this which uses safe smart defaults for everything.  Now a test suite, test class and test method at their respective levels can override the factory used.  Furthermore their factories might be able to leverage other factories from their superiors.</p>

<p>Think about this case.  You have a test suite A with test classes Foo and Bar in it.  Suite A defines a factory which overrides the system default.  Foo overrides this with it's own @Factory annotation, yet Foo's factory can access the factory of the suite.  So if it wanted too Foo's factory can use the test suite factory to instantiate the base object that it will alter.  This way Foo's does not have to reproduce the configuration code in the factory specified by it's superior Suite A.  This reuse capability can come in very handy.  Eventually a collection of factories will arise naturally enabling various features for testing.</p>

<p>Note that the CiRunner is responsible for figuring out which factory to use for each test that it runs.  The CiSuite which we will get into later delegates to the CiRunners and allows then to access it's settings.  The CiRunner must consider a parent test suite if it is included in one as well as the various SetupModes to determine when and how to use a Factory.  Once it does so it can use an instance of a factory to create new instances governed by the setup mode.</p>

<h3><a name="CoreIntegrationTestingFramework-LoadingDataforTests"></a>Loading Data for Tests</h3>

<p>Some test methods need data to already be present.  It's hard to document these kinds of prerequisites of a test method, its test class or a suite if the load of information is done via code.  It's also repetitive and error prone if done in code as well as tedious.  It makes no sense to do this by hand in code.</p>

<p>It would be really nice to have an <font color="#990000"><b>@ApplyLdif</b></font> annotation which would take an array of Strings arguments each element can be a valid LDAP operation encoded as an LDIF. This way the preload information is easy to document, explicitly stated and can be applied automatically by the runner without hand written code.  Here's an example of what this would look like:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">@ApplyLdifs ( {  <span class="code-comment">// First entry
</span>   <span class="code-quote">"dn: ou=test1,ou=system\n"</span> +
   <span class="code-quote">"changeType: add\n"</span> +
   <span class="code-quote">"objectClass: organizationalUnit\n"</span> +
   <span class="code-quote">"ou: test1\n\n"</span>,  <span class="code-comment">// Second entry
</span>   <span class="code-quote">"dn: ou=test2,ou=system\n"</span> +
   <span class="code-quote">"changeType: add\n"</span> +
   <span class="code-quote">"objectClass: organizationalUnit\n"</span> +
   <span class="code-quote">"ou: test2\n\n"</span> })
@Test
<span class="code-keyword">public</span> void deleteOu()
{
    delete( <span class="code-quote">"ou=test,ou=system"</span> );
}</pre>
</div></div>
<p>Above there was a single LDIF applied but more than one can be included and separated using the annotation syntax for arrays.  So a stream of operations can be conducted against the service before the test method is launched with control over the order of LDIF application.</p>

<p>Note that this annotation can be used at the Suite level, the Class level or the Method level.</p>
<table cellpadding='5' width='85%' cellspacing='8px' class='warningMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/forbidden.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td><b class="strong">Not Yet Implemented</b><br />
<p>The following annotation is not implemented yet.</p></td></tr></table>
<p>This is neat but it will not scale well.  It's obvious that it will get clunky after a while and should only be used for at most the trivial cases to prevent the java class file from really becoming an LDIF file.</p>

<p>So this leads us to yet another possibility.  How about another tag to bulk load LDIFs from a file.  We can use the <font color="#990000"><b>@ApplyLdifFiles</b></font> annotation to inform the runner of a set of LDIF files it should run in order.  This annotation is also an array of Strings but should probably hold the URL for the file.  Here's what it might look like:</p>
<div class="code"><div class="codeContent">
<pre class="code-java">@ApplyLdifFiles(
  {
     <span class="code-quote">"file:<span class="code-comment">///home/akarasulu/testdata/organizations.ldif"</span>,
</span>     <span class="code-quote">"file:<span class="code-comment">///home/akarasulu/testdata/users.ldif"</span>
</span>  }
)
@Test
<span class="code-keyword">public</span> void deleteEntries()
{
    delete( <span class="code-quote">"o=Apache,ou=system"</span> );
    delete( <span class="code-quote">"o=Eclipse,ou=system"</span> );
    delete( <span class="code-quote">"o=OpenLDAP,ou=system"</span> );

    delete( <span class="code-quote">"uid=akarasulu,ou=users,ou=system"</span> );
    delete( <span class="code-quote">"uid=elecharny,ou=users,ou=system"</span> );
    delete( <span class="code-quote">"uid=szoerner,ou=users,ou=system"</span> );
}</pre>
</div></div>
<p>This mechanism can be used for more than just loading data.  It can also be used to modify, rename, move and delete entries in the directory through the various changeTypes supported by the LDIF.  This certainly is a powerful feature to have.</p>

<p>One question though does come up and it has to do with defaults and hierarchy.  Should this aspect also follow the same patterns that the SetupMode, and Factory Annotations use or are their some particulars to consider specifically with this mechanism?  Yes I think there are particulars because there are more options.  First LDIFs can be combined instead of just being replaced.  The LDIF preparatory data could be additive or can replace defaults as it does with the other properties.  I really don't know what is best here.  I can see it being useful in both ways.</p>
<table cellpadding='5' width='85%' cellspacing='8px' class='noteMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/warning.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td><b class="strong">"Cannot Mix @ApplyLdifs with @ApplyLdifFiles"</b><br />
<p>The order of annotations are not preserved so ambiguity would be introduced if these two annotations were to be applied together.  Which goes first and is there a dependency between the two?  These kinds of questions make it even more difficult to implement this data loading feature in the same fashion that defaulting with inheritance is implemented with the other annotations.  Perhaps a choice must be made between having one or the other.  Or perhaps there needs to be a clearly defined rule like: all <font color="#990000"><b>@ApplyLdif</b></font> tags accumulated are applied first from parent objects down etc.</p></td></tr></table>
<p>Thoughts?<br/>
<table cellpadding='5' width='85%' cellspacing='8px' class='infoMacro' border="0" align='center'><colgroup><col width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16" height="16" align="absmiddle" alt="" border="0"></td><td>
<p>Comment by <span class="nobr"><a href="/confluence/display/~elecharny" title="View profile for Emmanuel L&#xC3;&#xA9;charny"><sup><img class="rendericon" src="/confluence/images/icons/user_12.gif" height="12" width="12" align="absmiddle" alt="" border="0"/></sup>Emmanuel Lécharny</a></span>.</p>

<p>The <font color="#990000"><b>@ApplyLdifFiles</b></font> is really interesting, but I'm just wondering if it can work with simple file names like test.ldif? The idea is to detect that we don't have a <span class="nobr"><a href="file://" title="Visit page outside Confluence" rel="nofollow">file://<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span> in from of the name, to be able to load the ldif associated with the test (it will be stored in src/test/resources/&lt;same package as the test&gt;/test.ldif )</p></td></tr></table>&nbsp;</p>

<h2><a name="CoreIntegrationTestingFramework-WhataboutSuites%3F"></a>What about Suites?</h2>

<p>The role of test suites is very important.  Suites will enable us to reduce integration test times even further.  I suspect a natural dynamic that will emerge where we will begin to group tests into suites because they use the same configuration.  This way the service only needs to be created and started once while using the <b>ROLLBACK</b> mode with the snapshot reverting feature.  This can have a dramatic impact.</p>

<p>However to do this the default trickle down and the way the service life cycle is managed in the different scopes of testing from suite level, class level to test method level will need to be clearly defined.  We need to be able to control situations where test classes will want to shutdown and destroy the service after all tests have already run.  These tests must not do so if there is a shared instance that is being used by the suite for all tests and test classes otherwise there is little benefit.</p>

<h2><a name="CoreIntegrationTestingFramework-RemoteExecution"></a>Remote Execution</h2>

<p>What does this mean?</p>
<ol>
	<li>It could mean using several machines to run the tests in parallel.</li>
	<li>It could mean using a DirectoryService implementation which is an adapter to forward real over the wire requests to a remote LDAP server that may or may not be ApacheDS.</li>
</ol>


<p>Both are cool concepts.  I really thought of (2) first.  As a user writing an LDAP driven application I may want to set up the unit tests to work with ApacheDS since it's embedded and easy.  But I may also want to run the same tests once and a while (say before releasing my application) on OpenLDAP, Active Directory, or SUN DS.  So it would be nice to have a generic DirectoryService object that acts as an adapter for over the wire requests.  The tests think this is not over the wire since it's the core they are testing.</p>

<p>For most black box tests the DirectoryService is really used as a factory for creating JNDI contexts.  This might change in the future but it's a good idea to think about how we can easily make the test code work against other service if that is something we desired.</p>

<p>Now option (1) is a really cool concept.  It would be pretty easy to pull off too.  Basically you need to have a service that sits on remote machines waiting for test requests.  Since we know what tests and suites will need to run in a single thread since we want to do one test at a time per instance we can determine how to easily parallelize tests.</p>

<p>Dynamically with a scheduler we can swap out some runners for a remote runner which for all practical purposes runs in the same way as the local ones do.  However instead the remote runner would contact the testing daemon on the remote host and request that it run a unit of tests that can be parallelized.  This would require working directory synchronization but it would not be that hard to do at all.  Furthermore a test daemon can always have a hot pristine service (sounds pornographic don't it?) ready and running for tests that can just start pounding it.</p>

<p>All good stuff - let's add that to the list of nice to have thingys.</p>

<h1><a name="CoreIntegrationTestingFramework-Implementation"></a>Implementation</h1>


<h2><a name="CoreIntegrationTestingFramework-APrototype"></a>A Prototype</h2>

<p>I've already begun implementing a working prototype in the bigbang and started using it for some tests.  Here's a link to the code:</p>

<p><span class="nobr"><a href="http://svn.apache.org/repos/asf/directory/apacheds/branches/bigbang/core-integ/src/main/java/org/apache/directory/server/core/integ/" title="Visit page outside Confluence" rel="nofollow">Bigbang SVN sources<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span></p>

<p>Note that once the bigbang is removed it will most likely be here:</p>

<p><span class="nobr"><a href="http://svn.apache.org/repos/asf/directory/apacheds/trunk/core-integ/src/main/java/org/apache/directory/server/core/integ/" title="Visit page outside Confluence" rel="nofollow">Trunk SVN sources(not yet existing)<sup><img class="rendericon" src="/confluence/images/icons/linkext7.gif" height="7" width="7" align="absmiddle" alt="" border="0"/></sup></a></span></p>

<p>Here's the current state of the prototype:</p>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> Tag </th>
<th class='confluenceTh'> State </th>
</tr>
<tr>
<td class='confluenceTd'> @Mode </td>
<td class='confluenceTd'> Done </td>
</tr>
<tr>
<td class='confluenceTd'> @Factory </td>
<td class='confluenceTd'> Done </td>
</tr>
</tbody></table>
<p>The <em>CiRunner</em> and the <em>CiSuite</em> have been written with the ability to process these tags above.  Yes there is much more work to be done.  They seem to work really well though.  I will continue working on this while I move integration tests over from core-unit to core-integ.  This will help me formulate a better approach as finer grained requirements start to appear as I convert tests.  When I'm done I will keep core-unit there for those that still prefer the old style of testing with JUnit 3.8.1 harnesses.</p>

<h2><a name="CoreIntegrationTestingFramework-DetailedSpecificationShouldComeFirst"></a>Detailed Specification Should Come First</h2>

<p>We need to define many things so this solution is easily implemented as well as maintained.  Everything from some information on JUnit 4 internals to how we intend to customize it.  Also we need to know exactly how we want the system to behave as we use Annotations to tweak the behavior without ambiguity.</p>

<h2><a name="CoreIntegrationTestingFramework-JUnit4ElementsofInterest"></a>JUnit 4 Elements of Interest</h2>


<h2><a name="CoreIntegrationTestingFramework-ConceptofTestServiceScope"></a>Concept of Test Service Scope</h2>


<h2><a name="CoreIntegrationTestingFramework-ConceptofSetupModeandtheEffectsofScope"></a>Concept of Setup Mode and the Effects of Scope</h2>


<h2><a name="CoreIntegrationTestingFramework-TestedServiceStates"></a>Tested Service States</h2>

<p>The states of a service used for testing are important and must be tracked.  We have to do a number of things based on the SetupMode, the scope of the test harness etc to determine what actions to take.  The if-then-else conditions can balloon out of control especially when we combine it with the runner life cycle in JUnit.  So it's a good idea for us to lists the states and draw a good old state diagram not to mention eventually using the State pattern in the implementation.</p>

<h3><a name="CoreIntegrationTestingFramework-States"></a>States</h3>

<p>Below pristine means we know the service is untouched; the disk image is the same way it is exactly as it was after a fresh new install.  Dirty means we don't know if it is pristine.  Reverted is not dirty but it's not pristine.  It appears for all practical purposes of the tests to be conducted to be pristine.</p>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> State </th>
<th class='confluenceTh'> Description </th>
</tr>
<tr>
<td class='confluenceTd'> NonExistant </td>
<td class='confluenceTd'> There is no service instance. </td>
</tr>
<tr>
<td class='confluenceTd'> StoppedDirtyState </td>
<td class='confluenceTd'> There is a service instance which is not running and we don't know if it's dirty. It is presumed that every newly created instance even is dirty. You may have instantiated the service on an old working directory. Who knows? </td>
</tr>
<tr>
<td class='confluenceTd'> StoppedPristineState </td>
<td class='confluenceTd'> The server is <b>not</b> running and is clean as if it was just installed </td>
</tr>
<tr>
<td class='confluenceTd'> StartedPristineState </td>
<td class='confluenceTd'> The server is running and is clean as if it were just installed </td>
</tr>
<tr>
<td class='confluenceTd'> StartedDirtyState </td>
<td class='confluenceTd'> The server is running but is not clean because changes have been made to it and it no longer resembles a running instance after a fresh new install. </td>
</tr>
<tr>
<td class='confluenceTd'> StartedRevertedState </td>
<td class='confluenceTd'> The server is running and looks for all practical purposes to the application being tested as if it was just installed.  Rather than being pristine it was reverted to it's the pristine state however this is not exactly the same as being pristine since the current revision number is not the same (revision numbers roll forward even when reverting to an earlier revision like subversion). </td>
</tr>
</tbody></table>

<h3><a name="CoreIntegrationTestingFramework-Actions"></a>Actions</h3>

<p>Actions represent the operations performed during the testing process which transition the tested service's state.  Hence they are transition arcs on a state diagram.  The actions are defined below:</p>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> Action </th>
<th class='confluenceTh'> Description </th>
</tr>
<tr>
<td class='confluenceTd'> CREATE </td>
<td class='confluenceTd'> The act of creating and configuring a service which boils down to using an instance of the DirectoryServiceFactory to get a ready to startup instance of the service. </td>
</tr>
<tr>
<td class='confluenceTd'> DESTROY </td>
<td class='confluenceTd'> The act of nulling out a reference to an already shutdown service and invoking the garbage collector. </td>
</tr>
<tr>
<td class='confluenceTd'> CLEANUP </td>
<td class='confluenceTd'> The act of deleting the working directory associated with a configured instance. </td>
</tr>
<tr>
<td class='confluenceTd'> STARTUP </td>
<td class='confluenceTd'> The act of starting a service which is not running. </td>
</tr>
<tr>
<td class='confluenceTd'> SHUTDOWN </td>
<td class='confluenceTd'> The act of stopping a service which is running. </td>
</tr>
<tr>
<td class='confluenceTd'> TEST </td>
<td class='confluenceTd'> The act of running tests against the directory service. </td>
</tr>
<tr>
<td class='confluenceTd'> REVERT </td>
<td class='confluenceTd'> The act of rolling back the server state to a point where the changes made by any tests are reversed. </td>
</tr>
</tbody></table>

<h3><a name="CoreIntegrationTestingFramework-StateDiagram"></a>State Diagram</h3>

<p>The following table expose all the different modes and states, with the associated action transitions :</p>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'>&nbsp;</th>
<th class='confluenceTh'> NotExistant </th>
<th class='confluenceTh'> StartedPristine </th>
<th class='confluenceTh'> StartedNormal </th>
</tr>
<tr>
<td class='confluenceTd'>&nbsp;</td>
<td class='confluenceTd'> action / next state </td>
<td class='confluenceTd'> action / next state </td>
<td class='confluenceTd'> action : next state </td>
</tr>
<tr>
<td class='confluenceTd'> PRISTINE </td>
<td class='confluenceTd'> NotExistant.create / NotExistant <br clear="all" />
 NotExistant.cleanup / NotExistant <br clear="all" />
 NotExistant.startup / StartedPristine <br clear="all" />
 StartedPristine.test </td>
<td class='confluenceTd'> <b>invokeTest</b> <br clear="all" />
 StartedPristine.shutdown / StartedPristine <br clear="all" />
 StartedPristine.cleanup / StartedPristine<br clear="all" />
 StartedPristine.destroy / NonExistant </td>
<td class='confluenceTd'>&nbsp;</td>
</tr>
<tr>
<td class='confluenceTd'> NOSERVICE </td>
<td class='confluenceTd'>&nbsp;</td>
<td class='confluenceTd'>&nbsp;</td>
<td class='confluenceTd'>&nbsp;</td>
</tr>
<tr>
<td class='confluenceTd'> ROLLBACK </td>
<td class='confluenceTd'> NotExistant.create / NotExistant <br clear="all" />
 NotExistant.cleanup / NotExistant<br clear="all" />
 NotExistant.startup / StartedPristine<br clear="all" />
 StartedPristine.test </td>
<td class='confluenceTd'> <b>tag</b><br clear="all" />
 <b>invokeTest</b> / StartedNormal<br clear="all" />
 StartedNormal.revert / StartedNormal </td>
<td class='confluenceTd'> <b>tag</b><br clear="all" />
 <b>invokeTest</b> / StartedNormal<br clear="all" />
 StartedNormal.revert / StartedNormal </td>
</tr>
<tr>
<td class='confluenceTd'> RESTART </td>
<td class='confluenceTd'> NotExistant.create / NotExistant<br clear="all" />
 NotExistant.startup / StartedNormal<br clear="all" />
 StartedNormal.test </td>
<td class='confluenceTd'>&nbsp;</td>
<td class='confluenceTd'> <b>invokeTest</b><br clear="all" />
 StartedNormal.shutdown / StartedNormal<br clear="all" />
 StartedNormal.startup / StartedNormal </td>
</tr>
<tr>
<td class='confluenceTd'> CUMULATIVE </td>
<td class='confluenceTd'> NotExistant.create / NotExistant<br clear="all" />
 NotExistant.startup / StartedNormal<br clear="all" />
 StartedNormal.test </td>
<td class='confluenceTd'>&nbsp;</td>
<td class='confluenceTd'> <b>invokeTest</b> </td>
</tr>
</tbody></table>

<p>Now let's mix the states and the actions together:</p>

<table class='confluenceTable'><tbody>
<tr>
<td class='confluenceTd'><div class="" align="center"><img src="/confluence/download/attachments/71929/UnitTestsPRISTINE.png" align="absmiddle" border="0" /></div></td>
<td class='confluenceTd'><div class="" align="center"><img src="/confluence/download/attachments/71929/UnitTestsROLLBACK.png" align="absmiddle" border="0" /></div></td>
</tr>
<tr>
<td class='confluenceTd'><div class="" align="center"><img src="/confluence/download/attachments/71929/UnitTestsRESTART.png" align="absmiddle" border="0" /></div></td>
<td class='confluenceTd'><div class="" align="center"><img src="/confluence/download/attachments/71929/UnitTestsCUMULATIVE.png" align="absmiddle" border="0" /></div></td>
</tr>
</tbody></table>


<h1><a name="CoreIntegrationTestingFramework-Appendix"></a>Appendix</h1>


<h2><a name="CoreIntegrationTestingFramework-AppendixA%3ASystemWideSetupModeDefault"></a>Appendix A: System Wide SetupMode Default</h2>

<p>The default setup mode to use is <b>ROLLBACK</b>.  Why you might ask is it not <b>NOSERVICE</b>?  The user shown us their intentions and has made a conscious decision to conduct tests with a running core service by selecting this Runner in the first place via the <font color="#990000"><b>@RunWith</b></font> JUnit annotation.  So by default it makes sense to just give them that if they decide to use it in any of the tests contained which is most likely the case otherwise why would they bother to use this runner.  The thing is some tests may need the service and some might not even use it.  If the <b>ROLLBACK</b> mode is used then there is no cost incurred for having a running server in the background.  The server will be used anyway at some point by some tests.  When not used by some tests there are no rollbacks and so the service just sits waiting for tests to use it.</p>
<ul>
	<li>If the default was <b>NOSERVICE</b> the user would have to do more work specify a mode on each test method that needs the core service running.</li>
	<li>If the default was <b>PRISTINE</b> then those tests not needing a service up would shutdown existing servers, clean up, create a new one and start the new service.  This process costs about 2-3 seconds churns the disk and costs memory. Turning off this default would require more effort on the user when we already know their intension's.</li>
	<li>If the default was <b>RESTART</b> then the impact would be similar to the pristine case with perhaps much less waste but still it would be very inefficient.</li>
	<li>If the default was <b>CUMULATIVE</b> then some test that may delete and create the same entries, often not worrying about cleanup would collide causing false negatives.</li>
</ul>


<p>As you can see the best option for the default is the <b>ROLLBACK</b> mode.  When a suite is present however mode inheritance can be utilized to override this default.</p></div>


</td></tr></table></div>
<p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
    <tr>
        <td height="12" background="http://cwiki.apache.org/confluence/images/border/border_bottom.gif"><img src="http://cwiki.apache.org/confluence/images/border/spacer.gif" width="1" height="1" border="0"/></td>
    </tr>
</table>

<div class="smalltext">
    Powered by
    <a href="http://www.atlassian.com/software/confluence/default.jsp?clicked=footer" class="smalltext">Atlassian Confluence</a>
    (Version: 2.2.9 Build:#527 Sep 07, 2006)
    -
    <a href="http://jira.atlassian.com/secure/BrowseProject.jspa?id=10470" class="smalltext">Bug/feature request</a><br/>
    <br>
    <a href="http://cwiki.apache.org/confluence/users/viewnotifications.action">Unsubscribe or edit your notifications preferences</a>

</div>

</body>
</html>


Mime
View raw message