Initial commit

This commit is contained in:
Mauricio Dinarte 2024-12-04 10:11:27 -06:00
commit c5e731d8ae
2773 changed files with 600767 additions and 0 deletions
drupal7/web/modules/simpletest
drupal_web_test_case.php
files
lib/Drupal/simpletest/Tests
simpletest.api.phpsimpletest.csssimpletest.infosimpletest.installsimpletest.jssimpletest.modulesimpletest.pages.incsimpletest.test
src/Tests
tests

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
These files are useful in tests that upload files or otherwise need to
manipulate files, in which case they are copied to the files directory as
specified in the site settings. Dummy files can also be generated by tests in
order to save space.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
.test1{display:block;}img[style*="float: right"]{padding-left:5px;}html .clear-block{height:1%;}.clear-block{display:block;font:italic bold 12px/30px Georgia,serif;}.test2{display:block;}.bkslshv1{background-color:#C00;}.test3{display:block;}.test4{display:block;}.comment-in-double-quotes:before{content:"/* ";}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-double-quotes:after{content:" */";}.comment-in-single-quotes:before{content:'/*';}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-single-quotes:after{content:'*/';}.comment-in-mixed-quotes:before{content:'"/*"';}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-mixed-quotes:after{content:"'*/'";}.comment-in-quotes-with-escaped:before{content:'/* \" \' */';}.this_rule_must_stay{color:#F00;background-color:#FFF;}.comment-in-quotes-with-escaped:after{content:"*/ \" \ '";}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,32 @@
@import url("http://example.com/style.css");
@import url("//example.com/style.css");
@import "import1.css";
@import "import2.css";
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,6 @@
@import url("http://example.com/style.css");@import url("//example.com/style.css");ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
.a
.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}

View file

@ -0,0 +1,57 @@
@import url("http://example.com/style.css");
@import url("//example.com/style.css");
ul, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
.ui-icon{background-image: url(images/icon.png);}
/* Test data URI images with different quote styles. */
.data .double-quote {
/* http://stackoverflow.com/a/13139830/11023 */
background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.data .single-quote {
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
}
.data .no-quote {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
}
p, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,69 @@
/**
* @file Basic css that does not use import
*/
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
/**
* CSS spec says that all whitespace is valid whitespace, so this selector
* should be just as good as the one above.
*/
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
some :pseudo .thing {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10');
-ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')";
}
::-moz-selection {
background: #000;
color:#fff;
}
::selection {
background: #000;
color: #fff;
}
@media print {
* {
background: #000 !important;
color: #fff !important;
}
@page {
margin: 0.5cm;
}
}
@media screen and (max-device-width: 480px) {
background: #000;
color: #fff;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,4 @@
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
.a
.test{font:1em/100% Verdana,sans-serif;color:#494949;}some :pseudo .thing{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#000000,direction='180',strength='10');-ms-filter:"progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')";}::-moz-selection{background:#000;color:#fff;}::selection{background:#000;color:#fff;}@media print{*{background:#000 !important;color:#fff !important;}@page{margin:0.5cm;}}@media screen and (max-device-width:480px){background:#000;color:#fff;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}

View file

@ -0,0 +1,69 @@
/**
* @file Basic css that does not use import
*/
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
/**
* CSS spec says that all whitespace is valid whitespace, so this selector
* should be just as good as the one above.
*/
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
some :pseudo .thing {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10');
-ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')";
}
::-moz-selection {
background: #000;
color:#fff;
}
::selection {
background: #000;
color: #fff;
}
@media print {
* {
background: #000 !important;
color: #fff !important;
}
@page {
margin: 0.5cm;
}
}
@media screen and (max-device-width: 480px) {
background: #000;
color: #fff;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,29 @@
@import "../import1.css";
@import "../import2.css";
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,6 @@
ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(../images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);}
p,select{font:1em/160% Verdana,sans-serif;color:#494949;}
body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this
.is
.a
.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;}

View file

@ -0,0 +1,54 @@
ul, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
.ui-icon{background-image: url(../images/icon.png);}
/* Test data URI images with different quote styles. */
.data .double-quote {
/* http://stackoverflow.com/a/13139830/11023 */
background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.data .single-quote {
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
}
.data .no-quote {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
}
p, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
body {
margin: 0;
padding: 0;
background: #edf5fa;
font: 76%/170% Verdana, sans-serif;
color: #494949;
}
.this .is .a .test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
.this
.is
.a
.test {
font: 1em/100% Verdana, sans-serif;
color: #494949;
}
textarea, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,20 @@
ul, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}
.ui-icon{background-image: url(images/icon.png);}
/* Test data URI images with different quote styles. */
.data .double-quote {
/* http://stackoverflow.com/a/13139830/11023 */
background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.data .single-quote {
background-image: url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');
}
.data .no-quote {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);
}

View file

@ -0,0 +1,5 @@
p, select {
font: 1em/160% Verdana, sans-serif;
color: #494949;
}

View file

@ -0,0 +1,31 @@
/* Example from https://www.w3.org/TR/CSS2/syndata.html#rule-sets */
q[example="public class foo\
{\
private int x;\
\
foo(int x) {\
this.x = x;\
}\
\
}"] { color: red }
/* A pseudo selector with essential whitespace wrapped in quotes. */
q[style*="quotes: none"] {
quotes: none;
}
q[style*='quotes: none'] {
quotes: none;
}
q:after {
content: ": colon & escaped double \" quotes \".";
}
q:after {
content: ' (brackets & escaped single \' quotes \') ';
}
q:after {
content: "I'm Quote";
}

View file

@ -0,0 +1,9 @@
q[example="public class foo\
{\
private int x;\
\
foo(int x) {\
this.x = x;\
}\
\
}"]{color:red}q[style*="quotes: none"]{quotes:none;}q[style*='quotes: none']{quotes:none;}q:after{content:": colon & escaped double \" quotes \".";}q:after{content:' (brackets & escaped single \' quotes \') ';}q:after{content:"I'm Quote";}

View file

@ -0,0 +1,31 @@
/* Example from https://www.w3.org/TR/CSS2/syndata.html#rule-sets */
q[example="public class foo\
{\
private int x;\
\
foo(int x) {\
this.x = x;\
}\
\
}"] { color: red }
/* A pseudo selector with essential whitespace wrapped in quotes. */
q[style*="quotes: none"] {
quotes: none;
}
q[style*='quotes: none'] {
quotes: none;
}
q:after {
content: ": colon & escaped double \" quotes \".";
}
q:after {
content: ' (brackets & escaped single \' quotes \') ';
}
q:after {
content: "I'm Quote";
}

View file

@ -0,0 +1 @@
<h1>SimpleTest HTML</h1>

View file

@ -0,0 +1 @@
<h1>SimpleTest HTML</h1>

Binary file not shown.

After

(image error) Size: 38 KiB

Binary file not shown.

After

(image error) Size: 1.8 KiB

Binary file not shown.

After

(image error) Size: 964 B

Binary file not shown.

After

(image error) Size: 183 B

Binary file not shown.

After

(image error) Size: 183 B

Binary file not shown.

After

(image error) Size: 1.9 KiB

Binary file not shown.

After

(image error) Size: 125 B

View file

@ -0,0 +1 @@
invalid image file

View file

@ -0,0 +1,3 @@
<script>
alert('SimpleTest PHP was executed!');
</script>

View file

@ -0,0 +1,3 @@
<script>
alert('SimpleTest PHP was executed!');
</script>

Binary file not shown.

View file

@ -0,0 +1,3 @@
<?php
print 'SimpleTest PHP was executed!';
?>

View file

@ -0,0 +1,2 @@
<?php
print 'SimpleTest PHP was executed!';

View file

@ -0,0 +1 @@
SELECT invalid_field FROM {invalid_table}

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\simpletest\Tests;
class PSR0WebTest extends \DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'PSR0 web test',
'description' => 'We want to assert that this PSR-0 test case is being discovered.',
'group' => 'SimpleTest',
);
}
function testArithmetics() {
$this->assert(1 + 1 == 2, '1 + 1 == 2');
}
}

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Hooks provided by the SimpleTest module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Alter the list of tests.
*
* @param $groups
* A two dimension array, the first key is the test group (as defined in
* getInfo) the second is the name of the class and the value is the return
* value of the getInfo method.
*/
function hook_simpletest_alter(&$groups) {
// An alternative session handler module would not want to run the original
// Session HTTPS handling test because it checks the sessions table in the
// database.
unset($groups['Session']['testHttpsSession']);
}
/**
* A test group has started.
*
* This hook is called just once at the beginning of a test group.
*/
function hook_test_group_started() {
}
/**
* A test group has finished.
*
* This hook is called just once at the end of a test group.
*/
function hook_test_group_finished() {
}
/**
* An individual test has finished.
*
* This hook is called when an individual test has finished.
*
* @param
* $results The results of the test as gathered by DrupalWebTestCase.
*
* @see DrupalWebTestCase->results
*/
function hook_test_finished($results) {
}
/**
* @} End of "addtogroup hooks".
*/

View file

@ -0,0 +1,89 @@
/* Test Table */
#simpletest-form-table th.select-all {
width: 1em;
}
th.simpletest_test {
width: 16em;
}
.simpletest-image {
display: inline-block;
cursor: pointer;
width: 1em;
}
.simpletest-group-label label {
display: inline;
font-weight: bold;
}
.simpletest-test-label label {
margin-left: 1em; /* LTR */
}
.simpletest-test-description .description {
margin: 0;
}
#simpletest-form-table tr td {
background-color: white;
color: #494949;
}
#simpletest-form-table tr.simpletest-group td {
background-color: #EDF5FA;
color: #494949;
}
table#simpletest-form-table tr.simpletest-group label {
display: inline;
}
div.message > div.item-list {
font-weight: normal;
}
div.simpletest-pass {
color: #33a333;
}
.simpletest-fail {
color: #981010;
}
tr.simpletest-pass.odd {
background-color: #b6ffb6;
}
tr.simpletest-pass.even {
background-color: #9bff9b;
}
tr.simpletest-fail.odd {
background-color: #ffc9c9;
}
tr.simpletest-fail.even {
background-color: #ffacac;
}
tr.simpletest-exception.odd {
background-color: #f4ea71;
}
tr.simpletest-exception.even {
background-color: #f5e742;
}
tr.simpletest-debug.odd {
background-color: #eee;
}
tr.simpletest-debug.even {
background-color: #fff;
}
a.simpletest-collapse {
height: 0;
width: 0;
top: -99em;
position: absolute;
}
a.simpletest-collapse:focus,
a.simpletest-collapse:hover {
font-size: 80%;
top: 0px;
height: auto;
width: auto;
overflow: visible;
position: relative;
z-index: 1000;
}

View file

@ -0,0 +1,64 @@
name = Testing
description = Provides a framework for unit and functional testing.
package = Core
version = VERSION
core = 7.x
files[] = simpletest.test
files[] = drupal_web_test_case.php
configure = admin/config/development/testing/settings
; Tests in tests directory.
files[] = tests/actions.test
files[] = tests/ajax.test
files[] = tests/batch.test
files[] = tests/boot.test
files[] = tests/bootstrap.test
files[] = tests/cache.test
files[] = tests/common.test
files[] = tests/database_test.test
files[] = tests/entity_crud.test
files[] = tests/entity_crud_hook_test.test
files[] = tests/entity_query.test
files[] = tests/error.test
files[] = tests/file.test
files[] = tests/filetransfer.test
files[] = tests/form.test
files[] = tests/graph.test
files[] = tests/image.test
files[] = tests/lock.test
files[] = tests/mail.test
files[] = tests/menu.test
files[] = tests/module.test
files[] = tests/pager.test
files[] = tests/password.test
files[] = tests/path.test
files[] = tests/registry.test
files[] = tests/request_sanitizer.test
files[] = tests/schema.test
files[] = tests/session.test
files[] = tests/tablesort.test
files[] = tests/theme.test
files[] = tests/unicode.test
files[] = tests/update.test
files[] = tests/xmlrpc.test
files[] = tests/upgrade/upgrade.test
files[] = tests/upgrade/upgrade.comment.test
files[] = tests/upgrade/upgrade.filter.test
files[] = tests/upgrade/upgrade.forum.test
files[] = tests/upgrade/upgrade.locale.test
files[] = tests/upgrade/upgrade.menu.test
files[] = tests/upgrade/upgrade.node.test
files[] = tests/upgrade/upgrade.taxonomy.test
files[] = tests/upgrade/upgrade.trigger.test
files[] = tests/upgrade/upgrade.translatable.test
files[] = tests/upgrade/upgrade.upload.test
files[] = tests/upgrade/upgrade.user.test
files[] = tests/upgrade/update.aggregator.test
files[] = tests/upgrade/update.trigger.test
files[] = tests/upgrade/update.field.test
files[] = tests/upgrade/update.user.test
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,169 @@
<?php
/**
* @file
* Install, update and uninstall functions for the simpletest module.
*/
/**
* Minimum value of PHP memory_limit for SimpleTest.
*/
define('SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT', '64M');
/**
* Implements hook_requirements().
*/
function simpletest_requirements($phase) {
$requirements = array();
$t = get_t();
$has_curl = function_exists('curl_init');
$has_hash = function_exists('hash_hmac');
$has_domdocument = method_exists('DOMDocument', 'loadHTML');
$requirements['curl'] = array(
'title' => $t('cURL'),
'value' => $has_curl ? $t('Enabled') : $t('Not found'),
);
if (!$has_curl) {
$requirements['curl']['severity'] = REQUIREMENT_ERROR;
$requirements['curl']['description'] = $t('The testing framework could not be installed because the PHP <a href="@curl_url">cURL</a> library is not available.', array('@curl_url' => 'http://php.net/manual/en/curl.setup.php'));
}
$requirements['hash'] = array(
'title' => $t('hash'),
'value' => $has_hash ? $t('Enabled') : $t('Not found'),
);
if (!$has_hash) {
$requirements['hash']['severity'] = REQUIREMENT_ERROR;
$requirements['hash']['description'] = $t('The testing framework could not be installed because the PHP <a href="@hash_url">hash</a> extension is disabled.', array('@hash_url' => 'http://php.net/manual/en/book.hash.php'));
}
$requirements['php_domdocument'] = array(
'title' => $t('PHP DOMDocument class'),
'value' => $has_domdocument ? $t('Enabled') : $t('Not found'),
);
if (!$has_domdocument) {
$requirements['php_domdocument']['severity'] = REQUIREMENT_ERROR;
$requirements['php_domdocument']['description'] = $t('The testing framework requires the DOMDocument class to be available. Check the configure command at the <a href="@link-phpinfo">PHP info page</a>.', array('@link-phpinfo' => url('admin/reports/status/php')));
}
// Check the current memory limit. If it is set too low, SimpleTest will fail
// to load all tests and throw a fatal error.
$memory_limit = ini_get('memory_limit');
if (!drupal_check_memory_limit(SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
$requirements['php_memory_limit']['severity'] = REQUIREMENT_ERROR;
$requirements['php_memory_limit']['description'] = $t('The testing framework requires the PHP memory limit to be at least %memory_minimum_limit. The current value is %memory_limit. <a href="@url">Follow these steps to continue</a>.', array('%memory_limit' => $memory_limit, '%memory_minimum_limit' => SIMPLETEST_MINIMUM_PHP_MEMORY_LIMIT, '@url' => 'http://drupal.org/node/207036'));
}
return $requirements;
}
/**
* Implements hook_schema().
*/
function simpletest_schema() {
$schema['simpletest'] = array(
'description' => 'Stores simpletest messages',
'fields' => array(
'message_id' => array(
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique simpletest message ID.',
),
'test_id' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Test ID, messages belonging to the same ID are reported together',
),
'test_class' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'The name of the class that created this message.',
),
'status' => array(
'type' => 'varchar',
'length' => 9,
'not null' => TRUE,
'default' => '',
'description' => 'Message status. Core understands pass, fail, exception.',
),
'message' => array(
'type' => 'text',
'not null' => TRUE,
'description' => 'The message itself.',
),
'message_group' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'The message group this message belongs to. For example: warning, browser, user.',
),
'function' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Name of the assertion function or method that created this message.',
),
'line' => array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'Line number on which the function is called.',
),
'file' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'description' => 'Name of the file where the function is called.',
),
),
'primary key' => array('message_id'),
'indexes' => array(
'reporter' => array('test_class', 'message_id'),
),
);
$schema['simpletest_test_id'] = array(
'description' => 'Stores simpletest test IDs, used to auto-incrament the test ID so that a fresh test ID is used.',
'fields' => array(
'test_id' => array(
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique simpletest ID used to group test results together. Each time a set of tests
are run a new test ID is used.',
),
'last_prefix' => array(
'type' => 'varchar',
'length' => 60,
'not null' => FALSE,
'default' => '',
'description' => 'The last database prefix used during testing.',
),
),
'primary key' => array('test_id'),
);
return $schema;
}
/**
* Implements hook_uninstall().
*/
function simpletest_uninstall() {
drupal_load('module', 'simpletest');
simpletest_clean_database();
// Remove settings variables.
variable_del('simpletest_httpauth_method');
variable_del('simpletest_httpauth_username');
variable_del('simpletest_httpauth_password');
variable_del('simpletest_clear_results');
variable_del('simpletest_verbose');
// Remove generated files.
file_unmanaged_delete_recursive('public://simpletest');
}

View file

@ -0,0 +1,104 @@
(function ($) {
/**
* Add the cool table collapsing on the testing overview page.
*/
Drupal.behaviors.simpleTestMenuCollapse = {
attach: function (context, settings) {
var timeout = null;
// Adds expand-collapse functionality.
$('div.simpletest-image').once('simpletest-image', function () {
var $this = $(this);
var direction = settings.simpleTest[this.id].imageDirection;
$this.html(settings.simpleTest.images[direction]);
// Adds group toggling functionality to arrow images.
$this.click(function () {
var trs = $this.closest('tbody').children('.' + settings.simpleTest[this.id].testClass);
var direction = settings.simpleTest[this.id].imageDirection;
var row = direction ? trs.length - 1 : 0;
// If clicked in the middle of expanding a group, stop so we can switch directions.
if (timeout) {
clearTimeout(timeout);
}
// Function to toggle an individual row according to the current direction.
// We set a timeout of 20 ms until the next row will be shown/hidden to
// create a sliding effect.
function rowToggle() {
if (direction) {
if (row >= 0) {
$(trs[row]).hide();
row--;
timeout = setTimeout(rowToggle, 20);
}
}
else {
if (row < trs.length) {
$(trs[row]).removeClass('js-hide').show();
row++;
timeout = setTimeout(rowToggle, 20);
}
}
}
// Kick-off the toggling upon a new click.
rowToggle();
// Toggle the arrow image next to the test group title.
$this.html(settings.simpleTest.images[(direction ? 0 : 1)]);
settings.simpleTest[this.id].imageDirection = !direction;
});
});
}
};
/**
* Select/deselect all the inner checkboxes when the outer checkboxes are
* selected/deselected.
*/
Drupal.behaviors.simpleTestSelectAll = {
attach: function (context, settings) {
$('td.simpletest-select-all').once('simpletest-select-all', function () {
var testCheckboxes = settings.simpleTest['simpletest-test-group-' + $(this).attr('id')].testNames;
var groupCheckbox = $('<input type="checkbox" class="form-checkbox" id="' + $(this).attr('id') + '-select-all" />');
// Each time a single-test checkbox is checked or unchecked, make sure
// that the associated group checkbox gets the right state too.
var updateGroupCheckbox = function () {
var checkedTests = 0;
for (var i = 0; i < testCheckboxes.length; i++) {
$('#' + testCheckboxes[i]).each(function () {
if (($(this).attr('checked'))) {
checkedTests++;
}
});
}
$(groupCheckbox).attr('checked', (checkedTests == testCheckboxes.length));
};
// Have the single-test checkboxes follow the group checkbox.
groupCheckbox.change(function () {
var checked = !!($(this).attr('checked'));
for (var i = 0; i < testCheckboxes.length; i++) {
$('#' + testCheckboxes[i]).attr('checked', checked);
}
});
// Have the group checkbox follow the single-test checkboxes.
for (var i = 0; i < testCheckboxes.length; i++) {
$('#' + testCheckboxes[i]).change(function () {
updateGroupCheckbox();
});
}
// Initialize status for the group checkbox correctly.
updateGroupCheckbox();
$(this).append(groupCheckbox);
});
}
};
})(jQuery);

View file

@ -0,0 +1,655 @@
<?php
/**
* @file
* Provides testing functionality.
*/
/**
* Implements hook_help().
*/
function simpletest_help($path, $arg) {
switch ($path) {
case 'admin/help#simpletest':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Testing module provides a framework for running automated unit tests. It can be used to verify a working state of Drupal before and after any code changes, or as a means for developers to write and execute tests for their modules. For more information, see the online handbook entry for <a href="@simpletest">Testing module</a>.', array('@simpletest' => 'http://drupal.org/documentation/modules/simpletest', '@blocks' => url('admin/structure/block'))) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Running tests') . '</dt>';
$output .= '<dd>' . t('Visit the <a href="@admin-simpletest">Testing page</a> to display a list of available tests. For comprehensive testing, select <em>all</em> tests, or individually select tests for more targeted testing. Note that it might take several minutes for all tests to complete. For more information on creating and modifying your own tests, see the <a href="@simpletest-api">Testing API Documentation</a> in the Drupal handbook.', array('@simpletest-api' => 'http://drupal.org/simpletest', '@admin-simpletest' => url('admin/config/development/testing'))) . '</dd>';
$output .= '<dd>' . t('After the tests run, a message will be displayed next to each test group indicating whether tests within it passed, failed, or had exceptions. A pass means that the test returned the expected results, while fail means that it did not. An exception normally indicates an error outside of the test, such as a PHP warning or notice. If there were failures or exceptions, the results will be expanded to show details, and the tests that had failures or exceptions will be indicated in red or pink rows. You can then use these results to refine your code and tests, until all tests pass.') . '</dd>';
$output .= '</dl>';
return $output;
}
}
/**
* Implements hook_menu().
*/
function simpletest_menu() {
$items['admin/config/development/testing'] = array(
'title' => 'Testing',
'page callback' => 'drupal_get_form',
'page arguments' => array('simpletest_test_form'),
'description' => 'Run tests against Drupal core and your active modules. These tests help assure that your site code is working as designed.',
'access arguments' => array('administer unit tests'),
'file' => 'simpletest.pages.inc',
'weight' => -5,
);
$items['admin/config/development/testing/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/development/testing/settings'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('simpletest_settings_form'),
'access arguments' => array('administer unit tests'),
'type' => MENU_LOCAL_TASK,
'file' => 'simpletest.pages.inc',
);
$items['admin/config/development/testing/results/%'] = array(
'title' => 'Test result',
'page callback' => 'drupal_get_form',
'page arguments' => array('simpletest_result_form', 5),
'description' => 'View result of tests.',
'access arguments' => array('administer unit tests'),
'file' => 'simpletest.pages.inc',
);
return $items;
}
/**
* Implements hook_permission().
*/
function simpletest_permission() {
return array(
'administer unit tests' => array(
'title' => t('Administer tests'),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_theme().
*/
function simpletest_theme() {
return array(
'simpletest_test_table' => array(
'render element' => 'table',
'file' => 'simpletest.pages.inc',
),
'simpletest_result_summary' => array(
'render element' => 'form',
'file' => 'simpletest.pages.inc',
),
);
}
/**
* Implements hook_js_alter().
*/
function simpletest_js_alter(&$javascript) {
// Since SimpleTest is a special use case for the table select, stick the
// SimpleTest JavaScript above the table select.
$simpletest = drupal_get_path('module', 'simpletest') . '/simpletest.js';
if (array_key_exists($simpletest, $javascript) && array_key_exists('misc/tableselect.js', $javascript)) {
$javascript[$simpletest]['weight'] = $javascript['misc/tableselect.js']['weight'] - 1;
}
}
function _simpletest_format_summary_line($summary) {
$args = array(
'@pass' => format_plural(isset($summary['#pass']) ? $summary['#pass'] : 0, '1 pass', '@count passes'),
'@fail' => format_plural(isset($summary['#fail']) ? $summary['#fail'] : 0, '1 fail', '@count fails'),
'@exception' => format_plural(isset($summary['#exception']) ? $summary['#exception'] : 0, '1 exception', '@count exceptions'),
);
if (!$summary['#debug']) {
return t('@pass, @fail, and @exception', $args);
}
$args['@debug'] = format_plural(isset($summary['#debug']) ? $summary['#debug'] : 0, '1 debug message', '@count debug messages');
return t('@pass, @fail, @exception, and @debug', $args);
}
/**
* Actually runs tests.
*
* @param $test_list
* List of tests to run.
* @param $reporter
* Which reporter to use. Allowed values are: text, xml, html and drupal,
* drupal being the default.
*/
function simpletest_run_tests($test_list, $reporter = 'drupal') {
$test_id = db_insert('simpletest_test_id')
->useDefaults(array('test_id'))
->execute();
// Clear out the previous verbose files.
file_unmanaged_delete_recursive('public://simpletest/verbose');
// Get the info for the first test being run.
$first_test = array_shift($test_list);
$first_instance = new $first_test();
array_unshift($test_list, $first_test);
$info = $first_instance->getInfo();
$batch = array(
'title' => t('Running tests'),
'operations' => array(
array('_simpletest_batch_operation', array($test_list, $test_id)),
),
'finished' => '_simpletest_batch_finished',
'progress_message' => '',
'css' => array(drupal_get_path('module', 'simpletest') . '/simpletest.css'),
'init_message' => t('Processing test @num of @max - %test.', array('%test' => $info['name'], '@num' => '1', '@max' => count($test_list))),
);
batch_set($batch);
module_invoke_all('test_group_started');
return $test_id;
}
/**
* Implements callback_batch_operation().
*/
function _simpletest_batch_operation($test_list_init, $test_id, &$context) {
simpletest_classloader_register();
// Get working values.
if (!isset($context['sandbox']['max'])) {
// First iteration: initialize working values.
$test_list = $test_list_init;
$context['sandbox']['max'] = count($test_list);
$test_results = array('#pass' => 0, '#fail' => 0, '#exception' => 0, '#debug' => 0);
}
else {
// Nth iteration: get the current values where we last stored them.
$test_list = $context['sandbox']['tests'];
$test_results = $context['sandbox']['test_results'];
}
$max = $context['sandbox']['max'];
// Perform the next test.
$test_class = array_shift($test_list);
$test = new $test_class($test_id);
$test->run();
$size = count($test_list);
$info = $test->getInfo();
module_invoke_all('test_finished', $test->results);
// Gather results and compose the report.
$test_results[$test_class] = $test->results;
foreach ($test_results[$test_class] as $key => $value) {
$test_results[$key] += $value;
}
$test_results[$test_class]['#name'] = $info['name'];
$items = array();
foreach (element_children($test_results) as $class) {
array_unshift($items, '<div class="simpletest-' . ($test_results[$class]['#fail'] + $test_results[$class]['#exception'] ? 'fail' : 'pass') . '">' . t('@name: @summary', array('@name' => $test_results[$class]['#name'], '@summary' => _simpletest_format_summary_line($test_results[$class]))) . '</div>');
}
$context['message'] = t('Processed test @num of @max - %test.', array('%test' => $info['name'], '@num' => $max - $size, '@max' => $max));
$context['message'] .= '<div class="simpletest-' . ($test_results['#fail'] + $test_results['#exception'] ? 'fail' : 'pass') . '">Overall results: ' . _simpletest_format_summary_line($test_results) . '</div>';
$context['message'] .= theme('item_list', array('items' => $items));
// Save working values for the next iteration.
$context['sandbox']['tests'] = $test_list;
$context['sandbox']['test_results'] = $test_results;
// The test_id is the only thing we need to save for the report page.
$context['results']['test_id'] = $test_id;
// Multistep processing: report progress.
$context['finished'] = 1 - $size / $max;
}
/**
* Implements callback_batch_finished().
*/
function _simpletest_batch_finished($success, $results, $operations, $elapsed) {
if ($success) {
drupal_set_message(t('The test run finished in @elapsed.', array('@elapsed' => $elapsed)));
}
else {
// Use the test_id passed as a parameter to _simpletest_batch_operation().
$test_id = $operations[0][1][1];
// Retrieve the last database prefix used for testing and the last test
// class that was run from. Use the information to read the log file
// in case any fatal errors caused the test to crash.
list($last_prefix, $last_test_class) = simpletest_last_test_get($test_id);
simpletest_log_read($test_id, $last_prefix, $last_test_class);
drupal_set_message(t('The test run did not successfully finish.'), 'error');
drupal_set_message(t('Use the <em>Clean environment</em> button to clean-up temporary files and tables.'), 'warning');
}
module_invoke_all('test_group_finished');
}
/**
* Get information about the last test that ran given a test ID.
*
* @param $test_id
* The test ID to get the last test from.
* @return
* Array containing the last database prefix used and the last test class
* that ran.
*/
function simpletest_last_test_get($test_id) {
$last_prefix = db_query_range('SELECT last_prefix FROM {simpletest_test_id} WHERE test_id = :test_id', 0, 1, array(':test_id' => $test_id))->fetchField();
$last_test_class = db_query_range('SELECT test_class FROM {simpletest} WHERE test_id = :test_id ORDER BY message_id DESC', 0, 1, array(':test_id' => $test_id))->fetchField();
return array($last_prefix, $last_test_class);
}
/**
* Read the error log and report any errors as assertion failures.
*
* The errors in the log should only be fatal errors since any other errors
* will have been recorded by the error handler.
*
* @param $test_id
* The test ID to which the log relates.
* @param $prefix
* The database prefix to which the log relates.
* @param $test_class
* The test class to which the log relates.
* @param $during_test
* Indicates that the current file directory path is a temporary file
* file directory used during testing.
* @return
* Found any entries in log.
*/
function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) {
$log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log';
$found = FALSE;
if (file_exists($log)) {
foreach (file($log) as $line) {
if (preg_match('/\[.*?\] (.*?): (.*?) in (.*) on line (\d+)/', $line, $match)) {
// Parse PHP fatal errors for example: PHP Fatal error: Call to
// undefined function break_me() in /path/to/file.php on line 17
$caller = array(
'line' => $match[4],
'file' => $match[3],
);
DrupalTestCase::insertAssert($test_id, $test_class, FALSE, $match[2], $match[1], $caller);
}
else {
// Unknown format, place the entire message in the log.
DrupalTestCase::insertAssert($test_id, $test_class, FALSE, $line, 'Fatal error');
}
$found = TRUE;
}
}
return $found;
}
/**
* Get a list of all of the tests provided by the system.
*
* The list of test classes is loaded from the registry where it looks for
* files ending in ".test". Once loaded the test list is cached and stored in
* a static variable. In order to list tests provided by disabled modules
* hook_registry_files_alter() is used to forcefully add them to the registry.
*
* PSR-0 classes are found by searching the designated directory for each module
* for files matching the PSR-0 standard.
*
* @return
* An array of tests keyed with the groups specified in each of the tests
* getInfo() method and then keyed by the test class. An example of the array
* structure is provided below.
*
* @code
* $groups['Blog'] => array(
* 'BlogTestCase' => array(
* 'name' => 'Blog functionality',
* 'description' => 'Create, view, edit, delete, ...',
* 'group' => 'Blog',
* ),
* );
* @endcode
* @see simpletest_registry_files_alter()
*/
function simpletest_test_get_all() {
$groups = &drupal_static(__FUNCTION__);
if (!$groups) {
// Register a simple class loader for PSR-0 test classes.
simpletest_classloader_register();
// Load test information from cache if available, otherwise retrieve the
// information from each tests getInfo() method.
if ($cache = cache_get('simpletest', 'cache')) {
$groups = $cache->data;
}
else {
// Select all classes in files ending with .test.
$classes = db_query("SELECT name FROM {registry} WHERE type = :type AND filename LIKE :name", array(':type' => 'class', ':name' => '%.test'))->fetchCol();
// Also discover PSR-0 test classes, if the PHP version allows it.
if (version_compare(PHP_VERSION, '5.3') > 0) {
// Select all PSR-0 and PSR-4 classes in the Tests namespace of all
// modules.
$system_list = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed();
foreach ($system_list as $name => $filename) {
$module_dir = DRUPAL_ROOT . '/' . dirname($filename);
// Search both the 'lib/Drupal/mymodule' directory (for PSR-0 classes)
// and the 'src' directory (for PSR-4 classes).
foreach(array('lib/Drupal/' . $name, 'src') as $subdir) {
// Build directory in which the test files would reside.
$tests_dir = $module_dir . '/' . $subdir . '/Tests';
// Scan it for test files if it exists.
if (is_dir($tests_dir)) {
$files = file_scan_directory($tests_dir, '/.*\.php/');
if (!empty($files)) {
foreach ($files as $file) {
// Convert the file name into the namespaced class name.
$replacements = array(
'/' => '\\',
$module_dir . '/' => '',
'lib/' => '',
'src/' => 'Drupal\\' . $name . '\\',
'.php' => '',
);
$classes[] = strtr($file->uri, $replacements);
}
}
}
}
}
}
// Check that each class has a getInfo() method and store the information
// in an array keyed with the group specified in the test information.
$groups = array();
foreach ($classes as $class) {
// Test classes need to implement getInfo() to be valid.
if (class_exists($class) && method_exists($class, 'getInfo')) {
$info = call_user_func(array($class, 'getInfo'));
// If this test class requires a non-existing module, skip it.
if (!empty($info['dependencies'])) {
foreach ($info['dependencies'] as $module) {
// Pass FALSE as fourth argument so no error gets created for
// the missing file.
$found_module = drupal_get_filename('module', $module, NULL, FALSE);
if (!$found_module) {
continue 2;
}
}
}
$groups[$info['group']][$class] = $info;
}
}
// Sort the groups and tests within the groups by name.
uksort($groups, 'strnatcasecmp');
foreach ($groups as $group => &$tests) {
uksort($tests, 'strnatcasecmp');
}
// Allow modules extending core tests to disable originals.
drupal_alter('simpletest', $groups);
cache_set('simpletest', $groups);
}
}
return $groups;
}
/*
* Register a simple class loader that can find D8-style PSR-0 test classes.
*
* Other PSR-0 class loading can happen in contrib, but those contrib class
* loader modules will not be enabled when testbot runs. So we need to do this
* one in core.
*/
function simpletest_classloader_register() {
// Prevent duplicate classloader registration.
static $first_run = TRUE;
if (!$first_run) {
return;
}
$first_run = FALSE;
// Only register PSR-0 class loading if we are on PHP 5.3 or higher.
if (version_compare(PHP_VERSION, '5.3') > 0) {
spl_autoload_register('_simpletest_autoload_psr4_psr0');
}
}
/**
* Autoload callback to find PSR-4 and PSR-0 test classes.
*
* Looks in the 'src/Tests' and in the 'lib/Drupal/mymodule/Tests' directory of
* modules for the class.
*
* This will only work on classes where the namespace is of the pattern
* "Drupal\$extension\Tests\.."
*/
function _simpletest_autoload_psr4_psr0($class) {
// Static cache for extension paths.
// This cache is lazily filled as soon as it is needed.
static $extensions;
// Check that the first namespace fragment is "Drupal\"
if (substr($class, 0, 7) === 'Drupal\\') {
// Find the position of the second namespace separator.
$pos = strpos($class, '\\', 7);
// Check that the third namespace fragment is "\Tests\".
if (substr($class, $pos, 7) === '\\Tests\\') {
// Extract the second namespace fragment, which we expect to be the
// extension name.
$extension = substr($class, 7, $pos - 7);
// Lazy-load the extension paths, both enabled and disabled.
if (!isset($extensions)) {
$extensions = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed();
}
// Check if the second namespace fragment is a known extension name.
if (isset($extensions[$extension])) {
// Split the class into namespace and classname.
$nspos = strrpos($class, '\\');
$namespace = substr($class, 0, $nspos);
$classname = substr($class, $nspos + 1);
// Try the PSR-4 location first, and the PSR-0 location as a fallback.
// Build the PSR-4 filepath where we expect the class to be defined.
$psr4_path = dirname($extensions[$extension]) . '/src/' .
str_replace('\\', '/', substr($namespace, strlen('Drupal\\' . $extension . '\\'))) . '/' .
str_replace('_', '/', $classname) . '.php';
// Include the file, if it does exist.
if (file_exists($psr4_path)) {
include $psr4_path;
}
else {
// Build the PSR-0 filepath where we expect the class to be defined.
$psr0_path = dirname($extensions[$extension]) . '/lib/' .
str_replace('\\', '/', $namespace) . '/' .
str_replace('_', '/', $classname) . '.php';
// Include the file, if it does exist.
if (file_exists($psr0_path)) {
include $psr0_path;
}
}
}
}
}
}
/**
* Implements hook_registry_files_alter().
*
* Add the test files for disabled modules so that we get a list containing
* all the available tests.
*/
function simpletest_registry_files_alter(&$files, $modules) {
foreach ($modules as $module) {
// Only add test files for disabled modules, as enabled modules should
// already include any test files they provide.
if (!$module->status) {
$dir = $module->dir;
if (!empty($module->info['files'])) {
foreach ($module->info['files'] as $file) {
if (substr($file, -5) == '.test') {
$files["$dir/$file"] = array('module' => $module->name, 'weight' => $module->weight);
}
}
}
}
}
}
/**
* Generate test file.
*/
function simpletest_generate_file($filename, $width, $lines, $type = 'binary-text') {
$text = '';
for ($i = 0; $i < $lines; $i++) {
// Generate $width - 1 characters to leave space for the "\n" character.
for ($j = 0; $j < $width - 1; $j++) {
switch ($type) {
case 'text':
$text .= chr(rand(32, 126));
break;
case 'binary':
$text .= chr(rand(0, 31));
break;
case 'binary-text':
default:
$text .= rand(0, 1);
break;
}
}
$text .= "\n";
}
// Create filename.
file_put_contents('public://' . $filename . '.txt', $text);
return $filename;
}
/**
* Remove all temporary database tables and directories.
*/
function simpletest_clean_environment() {
simpletest_clean_database();
simpletest_clean_temporary_directories();
if (variable_get('simpletest_clear_results', TRUE)) {
$count = simpletest_clean_results_table();
drupal_set_message(format_plural($count, 'Removed 1 test result.', 'Removed @count test results.'));
}
else {
drupal_set_message(t('Clear results is disabled and the test results table will not be cleared.'), 'warning');
}
// Detect test classes that have been added, renamed or deleted.
registry_rebuild();
cache_clear_all('simpletest', 'cache');
}
/**
* Removed prefixed tables from the database that are left over from crashed tests.
*/
function simpletest_clean_database() {
$tables = db_find_tables_d8(Database::getConnection()->prefixTables('{simpletest}') . '%');
$schema = drupal_get_schema_unprocessed('simpletest');
$count = 0;
foreach (array_diff_key($tables, $schema) as $table) {
// Strip the prefix and skip tables without digits following "simpletest",
// e.g. {simpletest_test_id}.
if (preg_match('/simpletest\d+.*/', $table, $matches)) {
db_drop_table($matches[0]);
$count++;
}
}
if ($count > 0) {
drupal_set_message(format_plural($count, 'Removed 1 leftover table.', 'Removed @count leftover tables.'));
}
else {
drupal_set_message(t('No leftover tables to remove.'));
}
}
/**
* Find all leftover temporary directories and remove them.
*/
function simpletest_clean_temporary_directories() {
$count = 0;
if (is_dir('public://simpletest')) {
$files = scandir('public://simpletest');
foreach ($files as $file) {
$path = 'public://simpletest/' . $file;
// Ensure that cache directories are cleaned as well.
if (is_dir($path) && (is_numeric($file) || strpos($file, '1c') === 0)) {
file_unmanaged_delete_recursive($path);
$count++;
}
}
}
if ($count > 0) {
drupal_set_message(format_plural($count, 'Removed 1 temporary directory.', 'Removed @count temporary directories.'));
}
else {
drupal_set_message(t('No temporary directories to remove.'));
}
}
/**
* Clear the test result tables.
*
* @param $test_id
* Test ID to remove results for, or NULL to remove all results.
* @return
* The number of results removed.
*/
function simpletest_clean_results_table($test_id = NULL) {
if (variable_get('simpletest_clear_results', TRUE)) {
if ($test_id) {
$count = db_query('SELECT COUNT(test_id) FROM {simpletest_test_id} WHERE test_id = :test_id', array(':test_id' => $test_id))->fetchField();
db_delete('simpletest')
->condition('test_id', $test_id)
->execute();
db_delete('simpletest_test_id')
->condition('test_id', $test_id)
->execute();
}
else {
$count = db_query('SELECT COUNT(test_id) FROM {simpletest_test_id}')->fetchField();
// Clear test results.
db_delete('simpletest')->execute();
db_delete('simpletest_test_id')->execute();
}
return $count;
}
return 0;
}
/**
* Implements hook_mail_alter().
*
* Aborts sending of messages with ID 'simpletest_cancel_test'.
*
* @see MailTestCase::testCancelMessage()
*/
function simpletest_mail_alter(&$message) {
if ($message['id'] == 'simpletest_cancel_test') {
$message['send'] = FALSE;
}
}

View file

@ -0,0 +1,513 @@
<?php
/**
* @file
* Page callbacks for simpletest module.
*/
/**
* List tests arranged in groups that can be selected and run.
*/
function simpletest_test_form($form) {
$form['tests'] = array(
'#type' => 'fieldset',
'#title' => t('Tests'),
'#description' => t('Select the test(s) or test group(s) you would like to run, and click <em>Run tests</em>.'),
);
$form['tests']['table'] = array(
'#theme' => 'simpletest_test_table',
);
// Generate the list of tests arranged by group.
$groups = simpletest_test_get_all();
foreach ($groups as $group => $tests) {
$form['tests']['table'][$group] = array(
'#collapsed' => TRUE,
);
foreach ($tests as $class => $info) {
$form['tests']['table'][$group][$class] = array(
'#type' => 'checkbox',
'#title' => $info['name'],
'#description' => $info['description'],
);
}
}
// Operation buttons.
$form['tests']['op'] = array(
'#type' => 'submit',
'#value' => t('Run tests'),
);
$form['clean'] = array(
'#type' => 'fieldset',
'#collapsible' => FALSE,
'#collapsed' => FALSE,
'#title' => t('Clean test environment'),
'#description' => t('Remove tables with the prefix "simpletest" and temporary directories that are left over from tests that crashed. This is intended for developers when creating tests.'),
);
$form['clean']['op'] = array(
'#type' => 'submit',
'#value' => t('Clean environment'),
'#submit' => array('simpletest_clean_environment'),
);
return $form;
}
/**
* Returns HTML for a test list generated by simpletest_test_form() into a table.
*
* @param $variables
* An associative array containing:
* - table: A render element representing the table.
*
* @ingroup themeable
*/
function theme_simpletest_test_table($variables) {
$table = $variables['table'];
drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js');
drupal_add_js('misc/tableselect.js');
// Create header for test selection table.
$header = array(
array('class' => array('select-all')),
array('data' => t('Test'), 'class' => array('simpletest_test')),
array('data' => t('Description'), 'class' => array('simpletest_description')),
);
// Define the images used to expand/collapse the test groups.
$js = array(
'images' => array(
theme('image', array('path' => 'misc/menu-collapsed.png', 'width' => 7, 'height' => 7, 'alt' => t('Expand'), 'title' => t('Expand'))) . ' <a href="#" class="simpletest-collapse">(' . t('Expand') . ')</a>',
theme('image', array('path' => 'misc/menu-expanded.png', 'width' => 7, 'height' => 7, 'alt' => t('Collapse'), 'title' => t('Collapse'))) . ' <a href="#" class="simpletest-collapse">(' . t('Collapse') . ')</a>',
),
);
// Cycle through each test group and create a row.
$rows = array();
foreach (element_children($table) as $key) {
$element = &$table[$key];
$row = array();
// Make the class name safe for output on the page by replacing all
// non-word/decimal characters with a dash (-).
$test_class = strtolower(trim(preg_replace("/[^\w\d]/", "-", $key)));
// Select the right "expand"/"collapse" image, depending on whether the
// category is expanded (at least one test selected) or not.
$collapsed = !empty($element['#collapsed']);
$image_index = $collapsed ? 0 : 1;
// Place-holder for checkboxes to select group of tests.
$row[] = array('id' => $test_class, 'class' => array('simpletest-select-all'));
// Expand/collapse image and group title.
$row[] = array(
'data' => '<div class="simpletest-image" id="simpletest-test-group-' . $test_class . '"></div>' .
'<label for="' . $test_class . '-select-all" class="simpletest-group-label">' . $key . '</label>',
'class' => array('simpletest-group-label'),
);
$row[] = array(
'data' => '&nbsp;',
'class' => array('simpletest-group-description'),
);
$rows[] = array('data' => $row, 'class' => array('simpletest-group'));
// Add individual tests to group.
$current_js = array(
'testClass' => $test_class . '-test',
'testNames' => array(),
'imageDirection' => $image_index,
'clickActive' => FALSE,
);
// Sorting $element by children's #title attribute instead of by class name.
uasort($element, 'element_sort_by_title');
// Cycle through each test within the current group.
foreach (element_children($element) as $test_name) {
$test = $element[$test_name];
$row = array();
$current_js['testNames'][] = $test['#id'];
// Store test title and description so that checkbox won't render them.
$title = $test['#title'];
$description = $test['#description'];
$test['#title_display'] = 'invisible';
unset($test['#description']);
// Test name is used to determine what tests to run.
$test['#name'] = $test_name;
$row[] = array(
'data' => drupal_render($test),
'class' => array('simpletest-test-select'),
);
$row[] = array(
'data' => '<label for="' . $test['#id'] . '">' . $title . '</label>',
'class' => array('simpletest-test-label'),
);
$row[] = array(
'data' => '<div class="description">' . $description . '</div>',
'class' => array('simpletest-test-description'),
);
$rows[] = array('data' => $row, 'class' => array($test_class . '-test', ($collapsed ? 'js-hide' : '')));
}
$js['simpletest-test-group-' . $test_class] = $current_js;
unset($table[$key]);
}
// Add js array of settings.
drupal_add_js(array('simpleTest' => $js), 'setting');
if (empty($rows)) {
return '<strong>' . t('No tests to display.') . '</strong>';
}
else {
return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'simpletest-form-table')));
}
}
/**
* Run selected tests.
*/
function simpletest_test_form_submit($form, &$form_state) {
simpletest_classloader_register();
// Get list of tests.
$tests_list = array();
foreach ($form_state['values'] as $class_name => $value) {
// Since class_exists() will likely trigger an autoload lookup,
// we do the fast check first.
if ($value === 1 && class_exists($class_name)) {
$tests_list[] = $class_name;
}
}
if (count($tests_list) > 0 ) {
$test_id = simpletest_run_tests($tests_list, 'drupal');
$form_state['redirect'] = 'admin/config/development/testing/results/' . $test_id;
}
else {
drupal_set_message(t('No test(s) selected.'), 'error');
}
}
/**
* Test results form for $test_id.
*/
function simpletest_result_form($form, &$form_state, $test_id) {
// Make sure there are test results to display and a re-run is not being performed.
$results = array();
if (is_numeric($test_id) && !$results = simpletest_result_get($test_id)) {
drupal_set_message(t('No test results to display.'), 'error');
drupal_goto('admin/config/development/testing');
return $form;
}
// Load all classes and include CSS.
drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
// Keep track of which test cases passed or failed.
$filter = array(
'pass' => array(),
'fail' => array(),
);
// Summary result fieldset.
$form['result'] = array(
'#type' => 'fieldset',
'#title' => t('Results'),
);
$form['result']['summary'] = $summary = array(
'#theme' => 'simpletest_result_summary',
'#pass' => 0,
'#fail' => 0,
'#exception' => 0,
'#debug' => 0,
);
simpletest_classloader_register();
// Cycle through each test group.
$header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status')));
$form['result']['results'] = array();
foreach ($results as $group => $assertions) {
// Create group fieldset with summary information.
$info = call_user_func(array($group, 'getInfo'));
$form['result']['results'][$group] = array(
'#type' => 'fieldset',
'#title' => $info['name'],
'#description' => $info['description'],
'#collapsible' => TRUE,
);
$form['result']['results'][$group]['summary'] = $summary;
$group_summary = &$form['result']['results'][$group]['summary'];
// Create table of assertions for the group.
$rows = array();
foreach ($assertions as $assertion) {
$row = array();
$row[] = $assertion->message;
$row[] = $assertion->message_group;
$row[] = drupal_basename($assertion->file);
$row[] = $assertion->line;
$row[] = $assertion->function;
$row[] = simpletest_result_status_image($assertion->status);
$class = 'simpletest-' . $assertion->status;
if ($assertion->message_group == 'Debug') {
$class = 'simpletest-debug';
}
$rows[] = array('data' => $row, 'class' => array($class));
$group_summary['#' . $assertion->status]++;
$form['result']['summary']['#' . $assertion->status]++;
}
$form['result']['results'][$group]['table'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
);
// Set summary information.
$group_summary['#ok'] = $group_summary['#fail'] + $group_summary['#exception'] == 0;
$form['result']['results'][$group]['#collapsed'] = $group_summary['#ok'];
// Store test group (class) as for use in filter.
$filter[$group_summary['#ok'] ? 'pass' : 'fail'][] = $group;
}
// Overal summary status.
$form['result']['summary']['#ok'] = $form['result']['summary']['#fail'] + $form['result']['summary']['#exception'] == 0;
// Actions.
$form['#action'] = url('admin/config/development/testing/results/re-run');
$form['action'] = array(
'#type' => 'fieldset',
'#title' => t('Actions'),
'#attributes' => array('class' => array('container-inline')),
'#weight' => -11,
);
$form['action']['filter'] = array(
'#type' => 'select',
'#title' => 'Filter',
'#options' => array(
'all' => t('All (@count)', array('@count' => count($filter['pass']) + count($filter['fail']))),
'pass' => t('Pass (@count)', array('@count' => count($filter['pass']))),
'fail' => t('Fail (@count)', array('@count' => count($filter['fail']))),
),
);
$form['action']['filter']['#default_value'] = ($filter['fail'] ? 'fail' : 'all');
// Categorized test classes for to be used with selected filter value.
$form['action']['filter_pass'] = array(
'#type' => 'hidden',
'#default_value' => implode(',', $filter['pass']),
);
$form['action']['filter_fail'] = array(
'#type' => 'hidden',
'#default_value' => implode(',', $filter['fail']),
);
$form['action']['op'] = array(
'#type' => 'submit',
'#value' => t('Run tests'),
);
$form['action']['return'] = array(
'#type' => 'link',
'#title' => t('Return to list'),
'#href' => 'admin/config/development/testing',
);
if (is_numeric($test_id)) {
simpletest_clean_results_table($test_id);
}
return $form;
}
/**
* Re-run the tests that match the filter.
*/
function simpletest_result_form_submit($form, &$form_state) {
$pass = $form_state['values']['filter_pass'] ? explode(',', $form_state['values']['filter_pass']) : array();
$fail = $form_state['values']['filter_fail'] ? explode(',', $form_state['values']['filter_fail']) : array();
if ($form_state['values']['filter'] == 'all') {
$classes = array_merge($pass, $fail);
}
elseif ($form_state['values']['filter'] == 'pass') {
$classes = $pass;
}
else {
$classes = $fail;
}
if (!$classes) {
$form_state['redirect'] = 'admin/config/development/testing';
return;
}
$form_state_execute = array('values' => array());
foreach ($classes as $class) {
$form_state_execute['values'][$class] = 1;
}
simpletest_test_form_submit(array(), $form_state_execute);
$form_state['redirect'] = $form_state_execute['redirect'];
}
/**
* Returns HTML for the summary status of a simpletest result.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @ingroup themeable
*/
function theme_simpletest_result_summary($variables) {
$form = $variables['form'];
return '<div class="simpletest-' . ($form['#ok'] ? 'pass' : 'fail') . '">' . _simpletest_format_summary_line($form) . '</div>';
}
/**
* Get test results for $test_id.
*
* @param $test_id The test_id to retrieve results of.
* @return Array of results grouped by test_class.
*/
function simpletest_result_get($test_id) {
$results = db_select('simpletest')
->fields('simpletest')
->condition('test_id', $test_id)
->orderBy('test_class')
->orderBy('message_id')
->execute();
$test_results = array();
foreach ($results as $result) {
if (!isset($test_results[$result->test_class])) {
$test_results[$result->test_class] = array();
}
$test_results[$result->test_class][] = $result;
}
return $test_results;
}
/**
* Get the appropriate image for the status.
*
* @param $status Status string, either: pass, fail, exception.
* @return HTML image or false.
*/
function simpletest_result_status_image($status) {
// $map does not use drupal_static() as its value never changes.
static $map;
if (!isset($map)) {
$map = array(
'pass' => theme('image', array('path' => 'misc/watchdog-ok.png', 'width' => 18, 'height' => 18, 'alt' => t('Pass'))),
'fail' => theme('image', array('path' => 'misc/watchdog-error.png', 'width' => 18, 'height' => 18, 'alt' => t('Fail'))),
'exception' => theme('image', array('path' => 'misc/watchdog-warning.png', 'width' => 18, 'height' => 18, 'alt' => t('Exception'))),
'debug' => theme('image', array('path' => 'misc/watchdog-warning.png', 'width' => 18, 'height' => 18, 'alt' => t('Debug'))),
);
}
if (isset($map[$status])) {
return $map[$status];
}
return FALSE;
}
/**
* Provides settings form for SimpleTest variables.
*
* @ingroup forms
* @see simpletest_settings_form_validate()
*/
function simpletest_settings_form($form, &$form_state) {
$form['general'] = array(
'#type' => 'fieldset',
'#title' => t('General'),
);
$form['general']['simpletest_clear_results'] = array(
'#type' => 'checkbox',
'#title' => t('Clear results after each complete test suite run'),
'#description' => t('By default SimpleTest will clear the results after they have been viewed on the results page, but in some cases it may be useful to leave the results in the database. The results can then be viewed at <em>admin/config/development/testing/[test_id]</em>. The test ID can be found in the database, simpletest table, or kept track of when viewing the results the first time. Additionally, some modules may provide more analysis or features that require this setting to be disabled.'),
'#default_value' => variable_get('simpletest_clear_results', TRUE),
);
$form['general']['simpletest_verbose'] = array(
'#type' => 'checkbox',
'#title' => t('Provide verbose information when running tests'),
'#description' => t('The verbose data will be printed along with the standard assertions and is useful for debugging. The verbose data will be erased between each test suite run. The verbose data output is very detailed and should only be used when debugging.'),
'#default_value' => variable_get('simpletest_verbose', TRUE),
);
$form['httpauth'] = array(
'#type' => 'fieldset',
'#title' => t('HTTP authentication'),
'#description' => t('HTTP auth settings to be used by the SimpleTest browser during testing. Useful when the site requires basic HTTP authentication.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['httpauth']['simpletest_httpauth_method'] = array(
'#type' => 'select',
'#title' => t('Method'),
'#options' => array(
CURLAUTH_BASIC => t('Basic'),
CURLAUTH_DIGEST => t('Digest'),
CURLAUTH_GSSNEGOTIATE => t('GSS negotiate'),
CURLAUTH_NTLM => t('NTLM'),
CURLAUTH_ANY => t('Any'),
CURLAUTH_ANYSAFE => t('Any safe'),
),
'#default_value' => variable_get('simpletest_httpauth_method', CURLAUTH_BASIC),
);
$username = variable_get('simpletest_httpauth_username');
$password = variable_get('simpletest_httpauth_password');
$form['httpauth']['simpletest_httpauth_username'] = array(
'#type' => 'textfield',
'#title' => t('Username'),
'#default_value' => $username,
);
if ($username && $password) {
$form['httpauth']['simpletest_httpauth_username']['#description'] = t('Leave this blank to delete both the existing username and password.');
}
$form['httpauth']['simpletest_httpauth_password'] = array(
'#type' => 'password',
'#title' => t('Password'),
);
if ($password) {
$form['httpauth']['simpletest_httpauth_password']['#description'] = t('To change the password, enter the new password here.');
}
return system_settings_form($form);
}
/**
* Validation handler for simpletest_settings_form().
*/
function simpletest_settings_form_validate($form, &$form_state) {
// If a username was provided but a password wasn't, preserve the existing
// password.
if (!empty($form_state['values']['simpletest_httpauth_username']) && empty($form_state['values']['simpletest_httpauth_password'])) {
$form_state['values']['simpletest_httpauth_password'] = variable_get('simpletest_httpauth_password', '');
}
// If a password was provided but a username wasn't, the credentials are
// incorrect, so throw an error.
if (empty($form_state['values']['simpletest_httpauth_username']) && !empty($form_state['values']['simpletest_httpauth_password'])) {
form_set_error('simpletest_httpauth_username', t('HTTP authentication credentials must include a username in addition to a password.'));
}
}

View file

@ -0,0 +1,829 @@
<?php
/**
* @file
* Tests for simpletest.module.
*/
class SimpleTestFunctionalTest extends DrupalWebTestCase {
/**
* The results array that has been parsed by getTestResults().
*/
protected $childTestResults;
/**
* Store the test ID from each test run for comparison, to ensure they are
* incrementing.
*/
protected $test_ids = array();
/**
* The pass message.
*/
protected $pass;
/**
* The fail message.
*/
protected $fail;
/**
* The valid permission.
*/
protected $valid_permission;
/**
* The invalid permission.
*/
protected $invalid_permission;
public static function getInfo() {
return array(
'name' => 'SimpleTest functionality',
'description' => "Test SimpleTest's web interface: check that the intended tests were run and ensure that test reports display the intended results. Also test SimpleTest's internal browser and API's both explicitly and implicitly.",
'group' => 'SimpleTest'
);
}
function setUp() {
if (!$this->inCURL()) {
parent::setUp('simpletest');
// Create and login user
$admin_user = $this->drupalCreateUser(array('administer unit tests'));
$this->drupalLogin($admin_user);
}
else {
parent::setUp('non_existent_module');
}
}
/**
* Test the internal browsers functionality.
*/
function testInternalBrowser() {
global $conf;
if (!$this->inCURL()) {
$this->drupalGet('node');
$this->assertTrue($this->drupalGetHeader('Date'), 'An HTTP header was received.');
$this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), 'Site title matches.');
$this->assertNoTitle('Foo', 'Site title does not match.');
// Make sure that we are locked out of the installer when prefixing
// using the user-agent header. This is an important security check.
global $base_url;
$this->drupalGet($base_url . '/install.php', array('external' => TRUE));
$this->assertResponse(403, 'Cannot access install.php with a "simpletest" user-agent header.');
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$headers = $this->drupalGetHeaders(TRUE);
$this->assertEqual(count($headers), 2, 'There was one intermediate request.');
$this->assertTrue(strpos($headers[0][':status'], '302') !== FALSE, 'Intermediate response code was 302.');
$this->assertFalse(empty($headers[0]['location']), 'Intermediate request contained a Location header.');
$this->assertEqual($this->getUrl(), $headers[0]['location'], 'HTTP redirect was followed');
$this->assertFalse($this->drupalGetHeader('Location'), 'Headers from intermediate request were reset.');
$this->assertResponse(200, 'Response code from intermediate request was reset.');
// Test the maximum redirection option.
$this->drupalLogout();
$edit = array(
'name' => $user->name,
'pass' => $user->pass_raw
);
variable_set('simpletest_maximum_redirects', 1);
$this->drupalPost('user?destination=user/logout', $edit, t('Log in'));
$headers = $this->drupalGetHeaders(TRUE);
$this->assertEqual(count($headers), 2, 'Simpletest stopped following redirects after the first one.');
}
}
/**
* Test validation of the User-Agent header we use to perform test requests.
*/
function testUserAgentValidation() {
if (!$this->inCURL()) {
global $base_url;
$simpletest_path = $base_url . '/' . drupal_get_path('module', 'simpletest');
$HTTP_path = $simpletest_path .'/tests/http.php?q=node';
$https_path = $simpletest_path .'/tests/https.php?q=node';
// Generate a valid simpletest User-Agent to pass validation.
$this->assertTrue(preg_match('/simpletest\d+/', $this->databasePrefix, $matches), 'Database prefix contains simpletest prefix.');
$test_ua = drupal_generate_test_ua($matches[0]);
$this->additionalCurlOptions = array(CURLOPT_USERAGENT => $test_ua);
// Test pages only available for testing.
$this->drupalGet($HTTP_path);
$this->assertResponse(200, 'Requesting http.php with a legitimate simpletest User-Agent returns OK.');
$this->drupalGet($https_path);
$this->assertResponse(200, 'Requesting https.php with a legitimate simpletest User-Agent returns OK.');
// Now slightly modify the HMAC on the header, which should not validate.
$this->additionalCurlOptions = array(CURLOPT_USERAGENT => $test_ua . 'X');
$this->drupalGet($HTTP_path);
$this->assertResponse(403, 'Requesting http.php with a bad simpletest User-Agent fails.');
$this->drupalGet($https_path);
$this->assertResponse(403, 'Requesting https.php with a bad simpletest User-Agent fails.');
// Use a real User-Agent and verify that the special files http.php and
// https.php can't be accessed.
$this->additionalCurlOptions = array(CURLOPT_USERAGENT => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12');
$this->drupalGet($HTTP_path);
$this->assertResponse(403, 'Requesting http.php with a normal User-Agent fails.');
$this->drupalGet($https_path);
$this->assertResponse(403, 'Requesting https.php with a normal User-Agent fails.');
}
}
/**
* Make sure that tests selected through the web interface are run and
* that the results are displayed correctly.
*/
function testWebTestRunner() {
$this->pass = t('SimpleTest pass.');
$this->fail = t('SimpleTest fail.');
$this->valid_permission = 'access content';
$this->invalid_permission = 'invalid permission';
if ($this->inCURL()) {
// Only run following code if this test is running itself through a CURL request.
$this->stubTest();
}
else {
// Run twice so test_ids can be accumulated.
for ($i = 0; $i < 2; $i++) {
// Run this test from web interface.
$this->drupalGet('admin/config/development/testing');
$edit = array();
$edit['SimpleTestFunctionalTest'] = TRUE;
$this->drupalPost(NULL, $edit, t('Run tests'));
// Parse results and confirm that they are correct.
$this->getTestResults();
$this->confirmStubTestResults();
}
// Regression test for #290316.
// Check that test_id is incrementing.
$this->assertTrue($this->test_ids[0] != $this->test_ids[1], 'Test ID is incrementing.');
}
}
/**
* Test to be run and the results confirmed.
*/
function stubTest() {
$this->pass($this->pass);
$this->fail($this->fail);
$this->drupalCreateUser(array($this->valid_permission));
$this->drupalCreateUser(array($this->invalid_permission));
$this->pass(t('Test ID is @id.', array('@id' => $this->testId)));
// Generates a warning.
$a = '';
foreach ($a as $b) {
}
// Call an assert function specific to that class.
$this->assertNothing();
// Generates 3 warnings inside a PHP function.
simplexml_load_string('<fake>');
debug('Foo', 'Debug');
}
/**
* Assert nothing.
*/
function assertNothing() {
$this->pass("This is nothing.");
}
/**
* Confirm that the stub test produced the desired results.
*/
function confirmStubTestResults() {
$this->assertAssertion(t('Enabled modules: %modules', array('%modules' => 'non_existent_module')), 'Other', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->setUp()');
$this->assertAssertion($this->pass, 'Other', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
$this->assertAssertion($this->fail, 'Other', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
$this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
$this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
// Check that a warning is caught by simpletest.
// The exact error message differs between PHP versions so we check only
// the presense of the 'foreach' statement.
$this->assertAssertion('foreach()', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
// Check that the backtracing code works for specific assert function.
$this->assertAssertion('This is nothing.', 'Other', 'Pass', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
// Check that errors that occur inside PHP internal functions are correctly reported.
// The exact error message differs between PHP versions so we check only
// the function name 'simplexml_load_string'.
$this->assertAssertion('simplexml_load_string', 'Warning', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
$this->assertAssertion("Debug: 'Foo'", 'Debug', 'Fail', 'simpletest.test', 'SimpleTestFunctionalTest->stubTest()');
$this->assertEqual('6 passes, 5 fails, 4 exceptions, and 1 debug message', $this->childTestResults['summary'], 'Stub test summary is correct');
$this->test_ids[] = $test_id = $this->getTestIdFromResults();
$this->assertTrue($test_id, 'Found test ID in results.');
}
/**
* Fetch the test id from the test results.
*/
function getTestIdFromResults() {
foreach ($this->childTestResults['assertions'] as $assertion) {
if (preg_match('@^Test ID is ([0-9]*)\.$@', $assertion['message'], $matches)) {
return $matches[1];
}
}
return NULL;
}
/**
* Assert that an assertion with the specified values is displayed
* in the test results.
*
* @param string $message Assertion message.
* @param string $type Assertion type.
* @param string $status Assertion status.
* @param string $file File where the assertion originated.
* @param string $functuion Function where the assertion originated.
* @return Assertion result.
*/
function assertAssertion($message, $type, $status, $file, $function) {
$message = trim(strip_tags($message));
$found = FALSE;
foreach ($this->childTestResults['assertions'] as $assertion) {
if ((strpos($assertion['message'], $message) !== FALSE) &&
$assertion['type'] == $type &&
$assertion['status'] == $status &&
$assertion['file'] == $file &&
$assertion['function'] == $function) {
$found = TRUE;
break;
}
}
return $this->assertTrue($found, format_string('Found assertion {"@message", "@type", "@status", "@file", "@function"}.', array('@message' => $message, '@type' => $type, '@status' => $status, "@file" => $file, "@function" => $function)));
}
/**
* Get the results from a test and store them in the class array $results.
*/
function getTestResults() {
$results = array();
if ($this->parse()) {
if ($fieldset = $this->getResultFieldSet()) {
// Code assumes this is the only test in group.
$results['summary'] = $this->asText($fieldset->div->div[1]);
$results['name'] = $this->asText($fieldset->legend);
$results['assertions'] = array();
$tbody = $fieldset->div->table->tbody;
foreach ($tbody->tr as $row) {
$assertion = array();
$assertion['message'] = $this->asText($row->td[0]);
$assertion['type'] = $this->asText($row->td[1]);
$assertion['file'] = $this->asText($row->td[2]);
$assertion['line'] = $this->asText($row->td[3]);
$assertion['function'] = $this->asText($row->td[4]);
$ok_url = file_create_url('misc/watchdog-ok.png');
$assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail';
$results['assertions'][] = $assertion;
}
}
}
$this->childTestResults = $results;
}
/**
* Get the fieldset containing the results for group this test is in.
*/
function getResultFieldSet() {
$fieldsets = $this->xpath('//fieldset');
$info = $this->getInfo();
foreach ($fieldsets as $fieldset) {
if ($this->asText($fieldset->legend) == $info['name']) {
return $fieldset;
}
}
return FALSE;
}
/**
* Extract the text contained by the element.
*
* @param $element
* Element to extract text from.
* @return
* Extracted text.
*/
function asText(SimpleXMLElement $element) {
if (!is_object($element)) {
return $this->fail('The element is not an element.');
}
return trim(html_entity_decode(strip_tags($element->asXML())));
}
/**
* Check if the test is being run from inside a CURL request.
*/
function inCURL() {
return (bool) drupal_valid_test_ua();
}
}
/**
* Test internal testing framework browser.
*/
class SimpleTestBrowserTestCase extends DrupalWebTestCase {
/**
* A flag indicating whether a cookie has been set in a test.
*
* @var bool
*/
protected static $cookieSet = FALSE;
public static function getInfo() {
return array(
'name' => 'SimpleTest browser',
'description' => 'Test the internal browser of the testing framework.',
'group' => 'SimpleTest',
);
}
function setUp() {
parent::setUp();
variable_set('user_register', USER_REGISTER_VISITORS);
}
/**
* Test DrupalWebTestCase::getAbsoluteUrl().
*/
function testGetAbsoluteUrl() {
// Testbed runs with Clean URLs disabled, so disable it here.
variable_set('clean_url', 0);
$url = 'user/login';
$this->drupalGet($url);
$absolute = url($url, array('absolute' => TRUE));
$this->assertEqual($absolute, $this->url, 'Passed and requested URL are equal.');
$this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), 'Requested and returned absolute URL are equal.');
$this->drupalPost(NULL, array(), t('Log in'));
$this->assertEqual($absolute, $this->url, 'Passed and requested URL are equal.');
$this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), 'Requested and returned absolute URL are equal.');
$this->clickLink('Create new account');
$url = 'user/register';
$absolute = url($url, array('absolute' => TRUE));
$this->assertEqual($absolute, $this->url, 'Passed and requested URL are equal.');
$this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), 'Requested and returned absolute URL are equal.');
}
/**
* Tests XPath escaping.
*/
function testXPathEscaping() {
$testpage = <<< EOF
<html>
<body>
<a href="link1">A "weird" link, just to bother the dumb "XPath 1.0"</a>
<a href="link2">A second "even more weird" link, in memory of George O'Malley</a>
</body>
</html>
EOF;
$this->drupalSetContent($testpage);
// Matches the first link.
$urls = $this->xpath('//a[text()=:text]', array(':text' => 'A "weird" link, just to bother the dumb "XPath 1.0"'));
$this->assertEqual($urls[0]['href'], 'link1', 'Match with quotes.');
$urls = $this->xpath('//a[text()=:text]', array(':text' => 'A second "even more weird" link, in memory of George O\'Malley'));
$this->assertEqual($urls[0]['href'], 'link2', 'Match with mixed single and double quotes.');
}
/**
* Tests that cookies set during a request are available for testing.
*/
public function testCookies() {
// Check that the $this->cookies property is populated when a user logs in.
$user = $this->drupalCreateUser();
$edit = array('name' => $user->name, 'pass' => $user->pass_raw);
$this->drupalPost('<front>', $edit, t('Log in'));
$this->assertEqual(count($this->cookies), 1, 'A cookie is set when the user logs in.');
// Check that the name and value of the cookie match the request data.
$cookie_header = $this->drupalGetHeader('set-cookie', TRUE);
// The name and value are located at the start of the string, separated by
// an equals sign and ending in a semicolon.
preg_match('/^([^=]+)=([^;]+)/', $cookie_header, $matches);
$name = $matches[1];
$value = $matches[2];
$this->assertTrue(array_key_exists($name, $this->cookies), 'The cookie name is correct.');
$this->assertEqual($value, $this->cookies[$name]['value'], 'The cookie value is correct.');
// Set a flag indicating that a cookie has been set in this test.
// @see SimpleTestBrowserTestCase::testCookieDoesNotBleed().
self::$cookieSet = TRUE;
}
/**
* Tests that the cookies from a previous test do not bleed into a new test.
*
* @see SimpleTestBrowserTestCase::testCookies().
*/
public function testCookieDoesNotBleed() {
// In order for this test to be effective it should always run after the
// testCookies() test.
$this->assertTrue(self::$cookieSet, 'Tests have been executed in the expected order.');
$this->assertEqual(count($this->cookies), 0, 'No cookies are present at the start of a new test.');
}
}
class SimpleTestMailCaptureTestCase extends DrupalWebTestCase {
/**
* Implement getInfo().
*/
public static function getInfo() {
return array(
'name' => 'SimpleTest e-mail capturing',
'description' => 'Test the SimpleTest e-mail capturing logic, the assertMail assertion and the drupalGetMails function.',
'group' => 'SimpleTest',
);
}
/**
* Test to see if the wrapper function is executed correctly.
*/
function testMailSend() {
// Create an e-mail.
$subject = $this->randomString(64);
$body = $this->randomString(128);
$message = array(
'id' => 'drupal_mail_test',
'headers' => array('Content-type'=> 'text/html'),
'subject' => $subject,
'to' => 'foobar@example.com',
'body' => $body,
);
// Before we send the e-mail, drupalGetMails should return an empty array.
$captured_emails = $this->drupalGetMails();
$this->assertEqual(count($captured_emails), 0, 'The captured e-mails queue is empty.', 'E-mail');
// Send the e-mail.
$response = drupal_mail_system('simpletest', 'drupal_mail_test')->mail($message);
// Ensure that there is one e-mail in the captured e-mails array.
$captured_emails = $this->drupalGetMails();
$this->assertEqual(count($captured_emails), 1, 'One e-mail was captured.', 'E-mail');
// Assert that the e-mail was sent by iterating over the message properties
// and ensuring that they are captured intact.
foreach ($message as $field => $value) {
$this->assertMail($field, $value, format_string('The e-mail was sent and the value for property @field is intact.', array('@field' => $field)), 'E-mail');
}
// Send additional e-mails so more than one e-mail is captured.
for ($index = 0; $index < 5; $index++) {
$message = array(
'id' => 'drupal_mail_test_' . $index,
'headers' => array('Content-type'=> 'text/html'),
'subject' => $this->randomString(64),
'to' => $this->randomName(32) . '@example.com',
'body' => $this->randomString(512),
);
drupal_mail_system('drupal_mail_test', $index)->mail($message);
}
// There should now be 6 e-mails captured.
$captured_emails = $this->drupalGetMails();
$this->assertEqual(count($captured_emails), 6, 'All e-mails were captured.', 'E-mail');
// Test different ways of getting filtered e-mails via drupalGetMails().
$captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test'));
$this->assertEqual(count($captured_emails), 1, 'Only one e-mail is returned when filtering by id.', 'E-mail');
$captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test', 'subject' => $subject));
$this->assertEqual(count($captured_emails), 1, 'Only one e-mail is returned when filtering by id and subject.', 'E-mail');
$captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test', 'subject' => $subject, 'from' => 'this_was_not_used@example.com'));
$this->assertEqual(count($captured_emails), 0, 'No e-mails are returned when querying with an unused from address.', 'E-mail');
// Send the last e-mail again, so we can confirm that the drupalGetMails-filter
// correctly returns all e-mails with a given property/value.
drupal_mail_system('drupal_mail_test', $index)->mail($message);
$captured_emails = $this->drupalGetMails(array('id' => 'drupal_mail_test_4'));
$this->assertEqual(count($captured_emails), 2, 'All e-mails with the same id are returned when filtering by id.', 'E-mail');
}
}
/**
* Test Folder creation
*/
class SimpleTestFolderTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Testing SimpleTest setUp',
'description' => "This test will check SimpleTest's treatment of hook_install during setUp. Image module is used for test.",
'group' => 'SimpleTest',
);
}
function setUp() {
return parent::setUp('image');
}
function testFolderSetup() {
$directory = file_default_scheme() . '://styles';
$this->assertTrue(file_prepare_directory($directory, FALSE), 'Directory created.');
}
}
/**
* Test required modules for tests.
*/
class SimpleTestMissingDependentModuleUnitTest extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Testing dependent module test',
'description' => 'This test should not load since it requires a module that is not found.',
'group' => 'SimpleTest',
'dependencies' => array('simpletest_missing_module'),
);
}
/**
* Ensure that this test will not be loaded despite its dependency.
*/
function testFail() {
$this->fail(t('Running test with missing required module.'));
}
}
/**
* Tests a test case that does not run parent::setUp() in its setUp() method.
*
* If a test case does not call parent::setUp(), running
* DrupalTestCase::tearDown() would destroy the main site's database tables.
* Therefore, we ensure that tests which are not set up properly are skipped.
*
* @see DrupalTestCase
*/
class SimpleTestBrokenSetUp extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Broken SimpleTest method',
'description' => 'Tests a test case that does not call parent::setUp().',
'group' => 'SimpleTest'
);
}
function setUp() {
// If the test is being run from the main site, set up normally.
if (!drupal_valid_test_ua()) {
parent::setUp('simpletest');
// Create and log in user.
$admin_user = $this->drupalCreateUser(array('administer unit tests'));
$this->drupalLogin($admin_user);
}
// If the test is being run from within simpletest, set up the broken test.
else {
$this->pass(t('The test setUp() method has been run.'));
// Don't call parent::setUp(). This should trigger an error message.
}
}
function tearDown() {
// If the test is being run from the main site, tear down normally.
if (!drupal_valid_test_ua()) {
parent::tearDown();
}
else {
// If the test is being run from within simpletest, output a message.
$this->pass(t('The tearDown() method has run.'));
}
}
/**
* Runs this test case from within the simpletest child site.
*/
function testBreakSetUp() {
// If the test is being run from the main site, run it again from the web
// interface within the simpletest child site.
if (!drupal_valid_test_ua()) {
$edit['SimpleTestBrokenSetUp'] = TRUE;
$this->drupalPost('admin/config/development/testing', $edit, t('Run tests'));
// Verify that the broken test and its tearDown() method are skipped.
$this->assertRaw(t('The test setUp() method has been run.'));
$this->assertRaw(t('The test cannot be executed because it has not been set up properly.'));
$this->assertNoRaw(t('The test method has run.'));
$this->assertNoRaw(t('The tearDown() method has run.'));
}
// If the test is being run from within simpletest, output a message.
else {
$this->pass(t('The test method has run.'));
}
}
}
/**
* Verifies that tests bundled with installation profile modules are found.
*/
class SimpleTestInstallationProfileModuleTestsTestCase extends DrupalWebTestCase {
/**
* Use the Testing profile.
*
* The Testing profile contains drupal_system_listing_compatible_test.test,
* which attempts to:
* - run tests using the Minimal profile (which does not contain the
* drupal_system_listing_compatible_test.module)
* - but still install the drupal_system_listing_compatible_test.module
* contained in the Testing profile.
*
* @see DrupalSystemListingCompatibleTestCase
*/
protected $profile = 'testing';
protected $admin_user;
public static function getInfo() {
return array(
'name' => 'Installation profile module tests',
'description' => 'Verifies that tests bundled with installation profile modules are found.',
'group' => 'SimpleTest',
);
}
function setUp() {
parent::setUp(array('simpletest'));
$this->admin_user = $this->drupalCreateUser(array('administer unit tests'));
$this->drupalLogin($this->admin_user);
}
/**
* Tests existence of test case located in an installation profile module.
*/
function testInstallationProfileTests() {
$this->drupalGet('admin/config/development/testing');
$this->assertText('Installation profile module tests helper');
$edit = array(
'DrupalSystemListingCompatibleTestCase' => TRUE,
);
$this->drupalPost(NULL, $edit, t('Run tests'));
$this->assertText('DrupalSystemListingCompatibleTestCase test executed.');
}
}
/**
* Verifies that tests in other installation profiles are not found.
*
* @see SimpleTestInstallationProfileModuleTestsTestCase
*/
class SimpleTestOtherInstallationProfileModuleTestsTestCase extends DrupalWebTestCase {
/**
* Use the Minimal profile.
*
* The Testing profile contains drupal_system_listing_compatible_test.test,
* which should not be found.
*
* @see SimpleTestInstallationProfileModuleTestsTestCase
* @see DrupalSystemListingCompatibleTestCase
*/
protected $profile = 'minimal';
protected $admin_user;
public static function getInfo() {
return array(
'name' => 'Other Installation profiles',
'description' => 'Verifies that tests in other installation profiles are not found.',
'group' => 'SimpleTest',
);
}
function setUp() {
parent::setUp(array('simpletest'));
$this->admin_user = $this->drupalCreateUser(array('administer unit tests'));
$this->drupalLogin($this->admin_user);
}
/**
* Tests that tests located in another installation profile do not appear.
*/
function testOtherInstallationProfile() {
$this->drupalGet('admin/config/development/testing');
$this->assertNoText('Installation profile module tests helper');
}
}
/**
* Verifies that tests in other installation profiles are not found.
*
* @see SimpleTestInstallationProfileModuleTestsTestCase
*/
class SimpleTestDiscoveryTestCase extends DrupalWebTestCase {
/**
* Use the Testing profile.
*
* The Testing profile contains drupal_system_listing_compatible_test.test,
* which attempts to:
* - run tests using the Minimal profile (which does not contain the
* drupal_system_listing_compatible_test.module)
* - but still install the drupal_system_listing_compatible_test.module
* contained in the Testing profile.
*
* @see DrupalSystemListingCompatibleTestCase
*/
protected $profile = 'testing';
protected $admin_user;
public static function getInfo() {
return array(
'name' => 'Discovery of test classes',
'description' => 'Verifies that tests classes are discovered and can be autoloaded (class_exists).',
'group' => 'SimpleTest',
);
}
function setUp() {
parent::setUp(array('simpletest'));
$this->admin_user = $this->drupalCreateUser(array('administer unit tests'));
$this->drupalLogin($this->admin_user);
}
/**
* Test discovery of PSR-0 test classes.
*/
function testDiscoveryFunctions() {
if (version_compare(PHP_VERSION, '5.3') < 0) {
// Don't expect PSR-0 tests to be discovered on older PHP versions.
return;
}
// TODO: What if we have cached values? Do we need to force a cache refresh?
$classes_all = simpletest_test_get_all();
foreach (array(
'Drupal\\simpletest\\Tests\\PSR0WebTest',
'Drupal\\simpletest\\Tests\\PSR4WebTest',
'Drupal\\psr_0_test\\Tests\\ExampleTest',
'Drupal\\psr_4_test\\Tests\\ExampleTest',
) as $class) {
$this->assert(!empty($classes_all['SimpleTest'][$class]), t('Class @class must be discovered by simpletest_test_get_all().', array('@class' => $class)));
}
}
/**
* Tests existence of test cases.
*/
function testDiscovery() {
$this->drupalGet('admin/config/development/testing');
// Tests within enabled modules.
// (without these, this test wouldn't happen in the first place, so this is
// a bit pointless. We still run it for proof-of-concept.)
// This one is defined in system module.
$this->assertText('Drupal error handlers');
// This one is defined in simpletest module.
$this->assertText('Discovery of test classes');
// Tests within disabled modules.
if (version_compare(PHP_VERSION, '5.3') < 0) {
// Don't expect PSR-0 tests to be discovered on older PHP versions.
return;
}
// These are provided by simpletest itself via PSR-0 and PSR-4.
$this->assertText('PSR0 web test');
$this->assertText('PSR4 web test');
$this->assertText('PSR0 example test: PSR-0 in disabled modules.');
$this->assertText('PSR4 example test: PSR-4 in disabled modules.');
$this->assertText('PSR0 example test: PSR-0 in nested subfolders.');
$this->assertText('PSR4 example test: PSR-4 in nested subfolders.');
// Test each test individually.
foreach (array(
'Drupal\\psr_0_test\\Tests\\ExampleTest',
'Drupal\\psr_0_test\\Tests\\Nested\\NestedExampleTest',
'Drupal\\psr_4_test\\Tests\\ExampleTest',
'Drupal\\psr_4_test\\Tests\\Nested\\NestedExampleTest',
) as $class) {
$this->drupalGet('admin/config/development/testing');
$edit = array($class => TRUE);
$this->drupalPost(NULL, $edit, t('Run tests'));
$this->assertText('The test run finished', t('Test @class must finish.', array('@class' => $class)));
$this->assertText('1 pass, 0 fails, and 0 exceptions', t('Test @class must pass.', array('@class' => $class)));
}
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\simpletest\Tests;
class PSR4WebTest extends \DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'PSR4 web test',
'description' => 'We want to assert that this PSR-4 test case is being discovered.',
'group' => 'SimpleTest',
);
}
function testArithmetics() {
$this->assert(1 + 1 == 2, '1 + 1 == 2');
}
}

View file

@ -0,0 +1,126 @@
<?php
class ActionsConfigurationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Actions configuration',
'description' => 'Tests complex actions configuration by adding, editing, and deleting a complex action.',
'group' => 'Actions',
);
}
/**
* Test the configuration of advanced actions through the administration
* interface.
*/
function testActionConfiguration() {
// Create a user with permission to view the actions administration pages.
$user = $this->drupalCreateUser(array('administer actions'));
$this->drupalLogin($user);
// Make a POST request to admin/config/system/actions/manage.
$edit = array();
$edit['action'] = drupal_hash_base64('system_goto_action');
$this->drupalPost('admin/config/system/actions/manage', $edit, t('Create'));
// Make a POST request to the individual action configuration page.
$edit = array();
$action_label = $this->randomName();
$edit['actions_label'] = $action_label;
$edit['url'] = 'admin';
$this->drupalPost('admin/config/system/actions/configure/' . drupal_hash_base64('system_goto_action'), $edit, t('Save'));
// Make sure that the new complex action was saved properly.
$this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action."));
$this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action."));
// Make another POST request to the action edit page.
$this->clickLink(t('configure'));
preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches);
$aid = $matches[1];
$edit = array();
$new_action_label = $this->randomName();
$edit['actions_label'] = $new_action_label;
$edit['url'] = 'admin';
$this->drupalPost(NULL, $edit, t('Save'));
// Make sure that the action updated properly.
$this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action."));
$this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."));
$this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action."));
// Make sure that deletions work properly.
$this->clickLink(t('delete'));
$edit = array();
$this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete'));
// Make sure that the action was actually deleted.
$this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.'));
$this->drupalGet('admin/config/system/actions/manage');
$this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action."));
$exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
$this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.'));
}
}
/**
* Test actions executing in a potential loop, and make sure they abort properly.
*/
class ActionLoopTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Actions executing in a potentially infinite loop',
'description' => 'Tests actions executing in a loop, and makes sure they abort properly.',
'group' => 'Actions',
);
}
function setUp() {
parent::setUp('dblog', 'trigger', 'actions_loop_test');
}
/**
* Set up a loop with 3 - 12 recursions, and see if it aborts properly.
*/
function testActionLoop() {
$user = $this->drupalCreateUser(array('administer actions'));
$this->drupalLogin($user);
$hash = drupal_hash_base64('actions_loop_test_log');
$edit = array('aid' => $hash);
$this->drupalPost('admin/structure/trigger/actions_loop_test', $edit, t('Assign'));
// Delete any existing watchdog messages to clear the plethora of
// "Action added" messages from when Drupal was installed.
db_delete('watchdog')->execute();
// To prevent this test from failing when xdebug is enabled, the maximum
// recursion level should be kept low enough to prevent the xdebug
// infinite recursion protection mechanism from aborting the request.
// See http://drupal.org/node/587634.
variable_set('actions_max_stack', 7);
$this->triggerActions();
}
/**
* Create an infinite loop by causing a watchdog message to be set,
* which causes the actions to be triggered again, up to actions_max_stack
* times.
*/
protected function triggerActions() {
$this->drupalGet('<front>', array('query' => array('trigger_actions_on_watchdog' => TRUE)));
$expected = array();
$expected[] = 'Triggering action loop';
for ($i = 1; $i <= variable_get('actions_max_stack', 35); $i++) {
$expected[] = "Test log #$i";
}
$expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.';
$result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid");
$loop_started = FALSE;
foreach ($result as $row) {
$expected_message = array_shift($expected);
$this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message)));
}
$this->assertTrue(empty($expected), t('All expected messages found.'));
}
}

View file

@ -0,0 +1,11 @@
name = Actions loop test
description = Support module for action loop testing.
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,11 @@
<?php
/**
* Implements hook_install().
*/
function actions_loop_test_install() {
db_update('system')
->fields(array('weight' => 1))
->condition('name', 'actions_loop_test')
->execute();
}

View file

@ -0,0 +1,95 @@
<?php
/**
* Implements hook_trigger_info().
*/
function actions_loop_test_trigger_info() {
return array(
'actions_loop_test' => array(
'watchdog' => array(
'label' => t('When a message is logged'),
),
),
);
}
/**
* Implements hook_watchdog().
*/
function actions_loop_test_watchdog(array $log_entry) {
// If the triggering actions are not explicitly enabled, abort.
if (empty($_GET['trigger_actions_on_watchdog'])) {
return;
}
// Get all the action ids assigned to the trigger on the watchdog hook's
// "run" event.
$aids = trigger_get_assigned_actions('watchdog');
// We can pass in any applicable information in $context. There isn't much in
// this case, but we'll pass in the hook name as the bare minimum.
$context = array(
'hook' => 'watchdog',
);
// Fire the actions on the associated object ($log_entry) and the context
// variable.
actions_do(array_keys($aids), $log_entry, $context);
}
/**
* Implements hook_init().
*/
function actions_loop_test_init() {
if (!empty($_GET['trigger_actions_on_watchdog'])) {
watchdog_skip_semaphore('actions_loop_test', 'Triggering action loop');
}
}
/**
* Implements hook_action_info().
*/
function actions_loop_test_action_info() {
return array(
'actions_loop_test_log' => array(
'label' => t('Write a message to the log.'),
'type' => 'system',
'configurable' => FALSE,
'triggers' => array('any'),
),
);
}
/**
* Write a message to the log.
*/
function actions_loop_test_log() {
$count = &drupal_static(__FUNCTION__, 0);
$count++;
watchdog_skip_semaphore('actions_loop_test', "Test log #$count");
}
/**
* Replacement of the watchdog() function that eliminates the use of semaphores
* so that we can test the abortion of an action loop.
*/
function watchdog_skip_semaphore($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
global $user, $base_root;
// Prepare the fields to be logged
$log_entry = array(
'type' => $type,
'message' => $message,
'variables' => $variables,
'severity' => $severity,
'link' => $link,
'user' => $user,
'uid' => isset($user->uid) ? $user->uid : 0,
'request_uri' => $base_root . request_uri(),
'referer' => $_SERVER['HTTP_REFERER'],
'ip' => ip_address(),
'timestamp' => REQUEST_TIME,
);
// Call the logging hooks to log/process the message
foreach (module_implements('watchdog') as $module) {
module_invoke($module, 'watchdog', $log_entry);
}
}

View file

@ -0,0 +1,621 @@
<?php
class AJAXTestCase extends DrupalWebTestCase {
function setUp() {
$modules = func_get_args();
if (isset($modules[0]) && is_array($modules[0])) {
$modules = $modules[0];
}
parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
}
/**
* Assert that a command with the required properties exists within the array of Ajax commands returned by the server.
*
* The Ajax framework, via the ajax_deliver() and ajax_render() functions,
* returns an array of commands. This array sometimes includes commands
* automatically provided by the framework in addition to commands returned by
* a particular page callback. During testing, we're usually interested that a
* particular command is present, and don't care whether other commands
* precede or follow the one we're interested in. Additionally, the command
* we're interested in may include additional data that we're not interested
* in. Therefore, this function simply asserts that one of the commands in
* $haystack contains all of the keys and values in $needle. Furthermore, if
* $needle contains a 'settings' key with an array value, we simply assert
* that all keys and values within that array are present in the command we're
* checking, and do not consider it a failure if the actual command contains
* additional settings that aren't part of $needle.
*
* @param $haystack
* An array of Ajax commands returned by the server.
* @param $needle
* Array of info we're expecting in one of those commands.
* @param $message
* An assertion message.
*/
protected function assertCommand($haystack, $needle, $message) {
$found = FALSE;
foreach ($haystack as $command) {
// If the command has additional settings that we're not testing for, do
// not consider that a failure.
if (isset($command['settings']) && is_array($command['settings']) && isset($needle['settings']) && is_array($needle['settings'])) {
$command['settings'] = array_intersect_key($command['settings'], $needle['settings']);
}
// If the command has additional data that we're not testing for, do not
// consider that a failure. Also, == instead of ===, because we don't
// require the key/value pairs to be in any particular order
// (http://www.php.net/manual/en/language.operators.array.php).
if (array_intersect_key($command, $needle) == $needle) {
$found = TRUE;
break;
}
}
$this->assertTrue($found, $message);
}
}
/**
* Tests primary Ajax framework functions.
*/
class AJAXFrameworkTestCase extends AJAXTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'AJAX framework',
'description' => 'Performs tests on AJAX framework functions.',
'group' => 'AJAX',
);
}
/**
* Test that ajax_render() returns JavaScript settings generated during the page request.
*
* @todo Add tests to ensure that ajax_render() returns commands for new CSS
* and JavaScript files to be loaded by the page. See
* http://drupal.org/node/561858.
*/
function testAJAXRender() {
$commands = $this->drupalGetAJAX('ajax-test/render');
// Verify that there is a command to load settings added with
// drupal_add_js().
$expected = array(
'command' => 'settings',
'settings' => array('basePath' => base_path(), 'ajax' => 'test'),
);
$this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().'));
// Verify that Ajax settings are loaded for #type 'link'.
$this->drupalGet('ajax-test/link');
$settings = $this->drupalGetSettings();
$this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips'));
$this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main');
}
/**
* Test behavior of ajax_render_error().
*/
function testAJAXRenderError() {
// Verify default error message.
$commands = $this->drupalGetAJAX('ajax-test/render-error');
$expected = array(
'command' => 'alert',
'text' => t('An error occurred while handling the request: The server received invalid input.'),
);
$this->assertCommand($commands, $expected, t('ajax_render_error() invokes alert command.'));
// Verify custom error message.
$edit = array(
'message' => 'Custom error message.',
);
$commands = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
$expected = array(
'command' => 'alert',
'text' => $edit['message'],
);
$this->assertCommand($commands, $expected, t('Custom error message is output.'));
}
/**
* Test that new JavaScript and CSS files added during an AJAX request are returned.
*/
function testLazyLoad() {
$expected = array(
'setting_name' => 'ajax_forms_test_lazy_load_form_submit',
'setting_value' => 'executed',
'css' => drupal_get_path('module', 'system') . '/system.admin.css',
'js' => drupal_get_path('module', 'system') . '/system.js',
);
// @todo D8: Add a drupal_css_defaults() helper function.
$expected_css_html = drupal_get_css(array($expected['css'] => array(
'type' => 'file',
'group' => CSS_DEFAULT,
'weight' => 0,
'every_page' => FALSE,
'media' => 'all',
'preprocess' => TRUE,
'data' => $expected['css'],
'browsers' => array('IE' => TRUE, '!IE' => TRUE),
)), TRUE);
$expected_js_html = drupal_get_js('header', array($expected['js'] => drupal_js_defaults($expected['js'])), TRUE);
// Get the base page.
$this->drupalGet('ajax_forms_test_lazy_load_form');
$original_settings = $this->drupalGetSettings();
$original_css = $original_settings['ajaxPageState']['css'];
$original_js = $original_settings['ajaxPageState']['js'];
// Verify that the base page doesn't have the settings and files that are to
// be lazy loaded as part of the next requests.
$this->assertTrue(!isset($original_settings[$expected['setting_name']]), t('Page originally lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
$this->assertTrue(!isset($original_settings[$expected['css']]), t('Page originally lacks the %css file, as expected.', array('%css' => $expected['css'])));
$this->assertTrue(!isset($original_settings[$expected['js']]), t('Page originally lacks the %js file, as expected.', array('%js' => $expected['js'])));
// Submit the AJAX request without triggering files getting added.
$commands = $this->drupalPostAJAX(NULL, array('add_files' => FALSE), array('op' => t('Submit')));
$new_settings = $this->drupalGetSettings();
// Verify the setting was not added when not expected.
$this->assertTrue(!isset($new_settings['setting_name']), t('Page still lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
// Verify a settings command does not add CSS or scripts to Drupal.settings
// and no command inserts the corresponding tags on the page.
$found_settings_command = FALSE;
$found_markup_command = FALSE;
foreach ($commands as $command) {
if ($command['command'] == 'settings' && (array_key_exists('css', $command['settings']['ajaxPageState']) || array_key_exists('js', $command['settings']['ajaxPageState']))) {
$found_settings_command = TRUE;
}
if (isset($command['data']) && ($command['data'] == $expected_js_html || $command['data'] == $expected_css_html)) {
$found_markup_command = TRUE;
}
}
$this->assertFalse($found_settings_command, t('Page state still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
$this->assertFalse($found_markup_command, t('Page still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
// Submit the AJAX request and trigger adding files.
$commands = $this->drupalPostAJAX(NULL, array('add_files' => TRUE), array('op' => t('Submit')));
$new_settings = $this->drupalGetSettings();
$new_css = $new_settings['ajaxPageState']['css'];
$new_js = $new_settings['ajaxPageState']['js'];
// Verify the expected setting was added.
$this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], t('Page now has the %setting.', array('%setting' => $expected['setting_name'])));
// Verify the expected CSS file was added, both to Drupal.settings, and as
// an AJAX command for inclusion into the HTML.
$this->assertEqual($new_css, $original_css + array($expected['css'] => 1), t('Page state now has the %css file.', array('%css' => $expected['css'])));
$this->assertCommand($commands, array('data' => $expected_css_html), t('Page now has the %css file.', array('%css' => $expected['css'])));
// Verify the expected JS file was added, both to Drupal.settings, and as
// an AJAX command for inclusion into the HTML. By testing for an exact HTML
// string containing the SCRIPT tag, we also ensure that unexpected
// JavaScript code, such as a jQuery.extend() that would potentially clobber
// rather than properly merge settings, didn't accidentally get added.
$this->assertEqual($new_js, $original_js + array($expected['js'] => 1), t('Page state now has the %js file.', array('%js' => $expected['js'])));
$this->assertCommand($commands, array('data' => $expected_js_html), t('Page now has the %js file.', array('%js' => $expected['js'])));
}
/**
* Tests that overridden CSS files are not added during lazy load.
*/
function testLazyLoadOverriddenCSS() {
// The test theme overrides system.base.css without an implementation,
// thereby removing it.
theme_enable(array('test_theme'));
variable_set('theme_default', 'test_theme');
// This gets the form, and emulates an Ajax submission on it, including
// adding markup to the HEAD and BODY for any lazy loaded JS/CSS files.
$this->drupalPostAJAX('ajax_forms_test_lazy_load_form', array('add_files' => TRUE), array('op' => t('Submit')));
// Verify that the resulting HTML does not load the overridden CSS file.
// We add a "?" to the assertion, because Drupal.settings may include
// information about the file; we only really care about whether it appears
// in a LINK or STYLE tag, for which Drupal always adds a query string for
// cache control.
$this->assertNoText('system.base.css?', 'Ajax lazy loading does not add overridden CSS files.');
}
}
/**
* Tests Ajax framework commands.
*/
class AJAXCommandsTestCase extends AJAXTestCase {
public static function getInfo() {
return array(
'name' => 'AJAX commands',
'description' => 'Performs tests on AJAX framework commands.',
'group' => 'AJAX',
);
}
/**
* Test the various Ajax Commands.
*/
function testAJAXCommands() {
$form_path = 'ajax_forms_test_ajax_commands_form';
$web_user = $this->drupalCreateUser(array('access content'));
$this->drupalLogin($web_user);
$edit = array();
// Tests the 'after' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div")));
$expected = array(
'command' => 'insert',
'method' => 'after',
'data' => 'This will be placed after',
);
$this->assertCommand($commands, $expected, "'after' AJAX command issued with correct data");
// Tests the 'alert' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert")));
$expected = array(
'command' => 'alert',
'text' => 'Alert',
);
$this->assertCommand($commands, $expected, "'alert' AJAX Command issued with correct text");
// Tests the 'append' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something")));
$expected = array(
'command' => 'insert',
'method' => 'append',
'data' => 'Appended text',
);
$this->assertCommand($commands, $expected, "'append' AJAX command issued with correct data");
// Tests the 'before' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div")));
$expected = array(
'command' => 'insert',
'method' => 'before',
'data' => 'Before text',
);
$this->assertCommand($commands, $expected, "'before' AJAX command issued with correct data");
// Tests the 'changed' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed.")));
$expected = array(
'command' => 'changed',
'selector' => '#changed_div',
);
$this->assertCommand($commands, $expected, "'changed' AJAX command issued with correct selector");
// Tests the 'changed' command using the second argument.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk.")));
$expected = array(
'command' => 'changed',
'selector' => '#changed_div',
'asterisk' => '#changed_div_mark_this',
);
$this->assertCommand($commands, $expected, "'changed' AJAX command (with asterisk) issued with correct selector");
// Tests the 'css' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the '#box' div to be blue.")));
$expected = array(
'command' => 'css',
'selector' => '#css_div',
'argument' => array('background-color' => 'blue'),
);
$this->assertCommand($commands, $expected, "'css' AJAX command issued with correct selector");
// Tests the 'data' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command.")));
$expected = array(
'command' => 'data',
'name' => 'testkey',
'value' => 'testvalue',
);
$this->assertCommand($commands, $expected, "'data' AJAX command issued with correct key and value");
// Tests the 'invoke' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX invoke command: Invoke addClass() method.")));
$expected = array(
'command' => 'invoke',
'method' => 'addClass',
'arguments' => array('error'),
);
$this->assertCommand($commands, $expected, "'invoke' AJAX command issued with correct method and argument");
// Tests the 'html' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector.")));
$expected = array(
'command' => 'insert',
'method' => 'html',
'data' => 'replacement text',
);
$this->assertCommand($commands, $expected, "'html' AJAX command issued with correct data");
// Tests the 'insert' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method'].")));
$expected = array(
'command' => 'insert',
'data' => 'insert replacement text',
);
$this->assertCommand($commands, $expected, "'insert' AJAX command issued with correct data");
// Tests the 'prepend' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something")));
$expected = array(
'command' => 'insert',
'method' => 'prepend',
'data' => 'prepended text',
);
$this->assertCommand($commands, $expected, "'prepend' AJAX command issued with correct data");
// Tests the 'remove' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text")));
$expected = array(
'command' => 'remove',
'selector' => '#remove_text',
);
$this->assertCommand($commands, $expected, "'remove' AJAX command issued with correct command and selector");
// Tests the 'restripe' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command")));
$expected = array(
'command' => 'restripe',
'selector' => '#restripe_table',
);
$this->assertCommand($commands, $expected, "'restripe' AJAX command issued with correct selector");
// Tests the 'settings' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'settings' command")));
$expected = array(
'command' => 'settings',
'settings' => array('ajax_forms_test' => array('foo' => 42)),
);
$this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data");
// Tests the 'add_css' command.
$commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'add_css' command")));
$expected = array(
'command' => 'add_css',
'data' => 'my/file.css',
);
$this->assertCommand($commands, $expected, "'add_css' AJAX command issued with correct data");
}
}
/**
* Test that $form_state['values'] is properly delivered to $ajax['callback'].
*/
class AJAXFormValuesTestCase extends AJAXTestCase {
protected $web_user;
public static function getInfo() {
return array(
'name' => 'AJAX command form values',
'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
'group' => 'AJAX',
);
}
function setUp() {
parent::setUp();
$this->web_user = $this->drupalCreateUser(array('access content'));
$this->drupalLogin($this->web_user);
}
/**
* Create a simple form, then POST to system/ajax to change to it.
*/
function testSimpleAJAXFormValue() {
// Verify form values of a select element.
foreach (array('red', 'green', 'blue') as $item) {
$edit = array(
'select' => $item,
);
$commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select');
$expected = array(
'command' => 'data',
'value' => $item,
);
$this->assertCommand($commands, $expected, "verification of AJAX form values from a selectbox issued with a correct value");
}
// Verify form values of a checkbox element.
foreach (array(FALSE, TRUE) as $item) {
$edit = array(
'checkbox' => $item,
);
$commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox');
$expected = array(
'command' => 'data',
'value' => (int) $item,
);
$this->assertCommand($commands, $expected, "verification of AJAX form values from a checkbox issued with a correct value");
}
}
}
/**
* Tests that Ajax-enabled forms work when multiple instances of the same form are on a page.
*/
class AJAXMultiFormTestCase extends AJAXTestCase {
protected $web_user;
public static function getInfo() {
return array(
'name' => 'AJAX multi form',
'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.',
'group' => 'AJAX',
);
}
function setUp() {
parent::setUp(array('form_test'));
// Create a multi-valued field for 'page' nodes to use for Ajax testing.
$field_name = 'field_ajax_test';
$field = array(
'field_name' => $field_name,
'type' => 'text',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
);
field_create_field($field);
$instance = array(
'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => 'page',
);
field_create_instance($instance);
// Login a user who can create 'page' nodes.
$this->web_user = $this->drupalCreateUser(array('create page content'));
$this->drupalLogin($this->web_user);
}
/**
* Test that a page with the 'page_node_form' included twice works correctly.
*/
function testMultiForm() {
// HTML IDs for elements within the field are potentially modified with
// each Ajax submission, but these variables are stable and help target the
// desired elements.
$field_name = 'field_ajax_test';
$field_xpaths = array(
'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]',
'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]',
);
$button_name = $field_name . '_add_more';
$button_value = t('Add another item');
$button_xpath_suffix = '//input[@name="' . $button_name . '"]';
$field_items_xpath_suffix = '//input[@type="text"]';
// Ensure the initial page contains both node forms and the correct number
// of field items and "add more" button for the multi-valued field within
// each form.
$this->drupalGet('form-test/two-instances-of-same-form');
foreach ($field_xpaths as $form_html_id => $field_xpath) {
$this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
$this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
}
$this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
// Submit the "add more" button of each form twice. After each corresponding
// page update, ensure the same as above.
foreach ($field_xpaths as $form_html_id => $field_xpath) {
for ($i = 0; $i < 2; $i++) {
$this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_html_id);
$this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
$this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
$this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
}
}
}
}
/**
* Test Ajax forms when page caching for anonymous users is turned on.
*/
class AJAXFormPageCacheTestCase extends AJAXTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'AJAX forms on cached pages',
'description' => 'Tests that AJAX forms work properly for anonymous users on cached pages.',
'group' => 'AJAX',
);
}
public function setUp() {
parent::setUp();
variable_set('cache', TRUE);
}
/**
* Return the build id of the current form.
*/
protected function getFormBuildId() {
$build_id_fields = $this->xpath('//input[@name="form_build_id"]');
$this->assertEqual(count($build_id_fields), 1, 'One form build id field on the page');
return (string) $build_id_fields[0]['value'];
}
/**
* Create a simple form, then POST to system/ajax to change to it.
*/
public function testSimpleAJAXFormValue() {
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$build_id_initial = $this->getFormBuildId();
$edit = array('select' => 'green');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_first_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
$expected = array(
'command' => 'updateBuildId',
'old' => $build_id_initial,
'new' => $build_id_first_ajax,
);
$this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
$edit = array('select' => 'red');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_second_ajax = $this->getFormBuildId();
$this->assertEqual($build_id_first_ajax, $build_id_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
// Repeat the test sequence but this time with a page loaded from the cache.
$this->drupalGet('ajax_forms_test_get_form');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$build_id_from_cache_initial = $this->getFormBuildId();
$this->assertEqual($build_id_initial, $build_id_from_cache_initial, 'Build id is the same as on the first request');
$edit = array('select' => 'green');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_from_cache_first_ajax = $this->getFormBuildId();
$this->assertNotEqual($build_id_from_cache_initial, $build_id_from_cache_first_ajax, 'Build id is changed in the simpletest-DOM on first AJAX submission');
$this->assertNotEqual($build_id_first_ajax, $build_id_from_cache_first_ajax, 'Build id from first user is not reused');
$expected = array(
'command' => 'updateBuildId',
'old' => $build_id_from_cache_initial,
'new' => $build_id_from_cache_first_ajax,
);
$this->assertCommand($commands, $expected, 'Build id change command issued on first AJAX submission');
$edit = array('select' => 'red');
$commands = $this->drupalPostAJAX(NULL, $edit, 'select');
$build_id_from_cache_second_ajax = $this->getFormBuildId();
$this->assertEqual($build_id_from_cache_first_ajax, $build_id_from_cache_second_ajax, 'Build id remains the same on subsequent AJAX submissions');
}
}
/**
* Miscellaneous Ajax tests using ajax_test module.
*/
class AJAXElementValidation extends AJAXTestCase {
public static function getInfo() {
return array(
'name' => 'Miscellaneous AJAX tests',
'description' => 'Various tests of AJAX behavior',
'group' => 'AJAX',
);
}
/**
* Try to post an Ajax change to a form that has a validated element.
*
* The drivertext field is Ajax-enabled. An additional field is not, but
* is set to be a required field. In this test the required field is not
* filled in, and we want to see if the activation of the "drivertext"
* Ajax-enabled field fails due to the required field being empty.
*/
function testAJAXElementValidation() {
$web_user = $this->drupalCreateUser();
$edit = array('drivertext' => t('some dumb text'));
// Post with 'drivertext' as the triggering element.
$post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext');
// Look for a validation failure in the resultant JSON.
$this->assertNoText(t('Error message'), "No error message in resultant JSON");
$this->assertText('ajax_forms_test_validation_form_callback invoked', 'The correct callback was invoked');
}
}

View file

@ -0,0 +1,11 @@
name = "AJAX form test mock module"
description = "Test for AJAX form calls."
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,520 @@
<?php
/**
* @file
* Simpletest mock module for Ajax forms testing.
*/
/**
* Implements hook_menu().
*/
function ajax_forms_test_menu() {
$items = array();
$items['ajax_forms_test_get_form'] = array(
'title' => 'AJAX forms simple form test',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_forms_test_simple_form'),
'access callback' => TRUE,
);
$items['ajax_forms_test_ajax_commands_form'] = array(
'title' => 'AJAX forms AJAX commands test',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_forms_test_ajax_commands_form'),
'access callback' => TRUE,
);
$items['ajax_validation_test'] = array(
'title' => 'AJAX Validation Test',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_forms_test_validation_form'),
'access callback' => TRUE,
);
$items['ajax_forms_test_lazy_load_form'] = array(
'title' => 'AJAX forms lazy load test',
'page callback' => 'drupal_get_form',
'page arguments' => array('ajax_forms_test_lazy_load_form'),
'access callback' => TRUE,
);
return $items;
}
/**
* A basic form used to test form_state['values'] during callback.
*/
function ajax_forms_test_simple_form($form, &$form_state) {
$form = array();
$form['select'] = array(
'#type' => 'select',
'#options' => array(
'red' => 'red',
'green' => 'green',
'blue' => 'blue'),
'#ajax' => array(
'callback' => 'ajax_forms_test_simple_form_select_callback',
),
'#suffix' => '<div id="ajax_selected_color">No color yet selected</div>',
);
$form['checkbox'] = array(
'#type' => 'checkbox',
'#title' => t('Test checkbox'),
'#ajax' => array(
'callback' => 'ajax_forms_test_simple_form_checkbox_callback',
),
'#suffix' => '<div id="ajax_checkbox_value">No action yet</div>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('submit'),
);
return $form;
}
/**
* Ajax callback triggered by select.
*/
function ajax_forms_test_simple_form_select_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_html('#ajax_selected_color', $form_state['values']['select']);
$commands[] = ajax_command_data('#ajax_selected_color', 'form_state_value_select', $form_state['values']['select']);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback triggered by checkbox.
*/
function ajax_forms_test_simple_form_checkbox_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_html('#ajax_checkbox_value', (int) $form_state['values']['checkbox']);
$commands[] = ajax_command_data('#ajax_checkbox_value', 'form_state_value_select', (int) $form_state['values']['checkbox']);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Form to display the Ajax Commands.
*/
function ajax_forms_test_ajax_commands_form($form, &$form_state) {
$form = array();
// Shows the 'after' command with a callback generating commands.
$form['after_command_example'] = array(
'#value' => t("AJAX 'After': Click to put something after the div"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_after_callback',
),
'#suffix' => '<div id="after_div">Something can be inserted after this</div>',
);
// Shows the 'alert' command.
$form['alert_command_example'] = array(
'#value' => t("AJAX 'Alert': Click to alert"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_alert_callback',
),
);
// Shows the 'append' command.
$form['append_command_example'] = array(
'#value' => t("AJAX 'Append': Click to append something"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_append_callback',
),
'#suffix' => '<div id="append_div">Append inside this div</div>',
);
// Shows the 'before' command.
$form['before_command_example'] = array(
'#value' => t("AJAX 'before': Click to put something before the div"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_before_callback',
),
'#suffix' => '<div id="before_div">Insert something before this.</div>',
);
// Shows the 'changed' command without asterisk.
$form['changed_command_example'] = array(
'#value' => t("AJAX changed: Click to mark div changed."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_changed_callback',
),
'#suffix' => '<div id="changed_div"> <div id="changed_div_mark_this">This div can be marked as changed or not.</div></div>',
);
// Shows the 'changed' command adding the asterisk.
$form['changed_command_asterisk_example'] = array(
'#value' => t("AJAX changed: Click to mark div changed with asterisk."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_changed_asterisk_callback',
),
);
// Shows the Ajax 'css' command.
$form['css_command_example'] = array(
'#value' => t("Set the '#box' div to be blue."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_css_callback',
),
'#suffix' => '<div id="css_div" style="height: 50px; width: 50px; border: 1px solid black"> box</div>',
);
// Shows the Ajax 'data' command. But there is no use of this information,
// as this would require a javascript client to use the data.
$form['data_command_example'] = array(
'#value' => t("AJAX data command: Issue command."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_data_callback',
),
'#suffix' => '<div id="data_div">Data attached to this div.</div>',
);
// Shows the Ajax 'invoke' command.
$form['invoke_command_example'] = array(
'#value' => t("AJAX invoke command: Invoke addClass() method."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_invoke_callback',
),
'#suffix' => '<div id="invoke_div">Original contents</div>',
);
// Shows the Ajax 'html' command.
$form['html_command_example'] = array(
'#value' => t("AJAX html: Replace the HTML in a selector."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_html_callback',
),
'#suffix' => '<div id="html_div">Original contents</div>',
);
// Shows the Ajax 'insert' command.
$form['insert_command_example'] = array(
'#value' => t("AJAX insert: Let client insert based on #ajax['method']."),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_insert_callback',
'method' => 'prepend',
),
'#suffix' => '<div id="insert_div">Original contents</div>',
);
// Shows the Ajax 'prepend' command.
$form['prepend_command_example'] = array(
'#value' => t("AJAX 'prepend': Click to prepend something"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_prepend_callback',
),
'#suffix' => '<div id="prepend_div">Something will be prepended to this div. </div>',
);
// Shows the Ajax 'remove' command.
$form['remove_command_example'] = array(
'#value' => t("AJAX 'remove': Click to remove text"),
'#type' => 'submit',
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_remove_callback',
),
'#suffix' => '<div id="remove_div"><div id="remove_text">text to be removed</div></div>',
);
// Shows the Ajax 'restripe' command.
$form['restripe_command_example'] = array(
'#type' => 'submit',
'#value' => t("AJAX 'restripe' command"),
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_restripe_callback',
),
'#suffix' => '<div id="restripe_div">
<table id="restripe_table" style="border: 1px solid black" >
<tr id="table-first"><td>first row</td></tr>
<tr ><td>second row</td></tr>
</table>
</div>',
);
// Demonstrates the Ajax 'settings' command. The 'settings' command has
// nothing visual to "show", but it can be tested via SimpleTest and via
// Firebug.
$form['settings_command_example'] = array(
'#type' => 'submit',
'#value' => t("AJAX 'settings' command"),
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_settings_callback',
),
);
// Shows the Ajax 'add_css' command.
$form['add_css_command_example'] = array(
'#type' => 'submit',
'#value' => t("AJAX 'add_css' command"),
'#ajax' => array(
'callback' => 'ajax_forms_test_advanced_commands_add_css_callback',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Ajax callback for 'after'.
*/
function ajax_forms_test_advanced_commands_after_callback($form, $form_state) {
$selector = '#after_div';
$commands = array();
$commands[] = ajax_command_after($selector, "This will be placed after");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'alert'.
*/
function ajax_forms_test_advanced_commands_alert_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_alert("Alert");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'append'.
*/
function ajax_forms_test_advanced_commands_append_callback($form, $form_state) {
$selector = '#append_div';
$commands = array();
$commands[] = ajax_command_append($selector, "Appended text");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'before'.
*/
function ajax_forms_test_advanced_commands_before_callback($form, $form_state) {
$selector = '#before_div';
$commands = array();
$commands[] = ajax_command_before($selector, "Before text");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'changed'.
*/
function ajax_forms_test_advanced_commands_changed_callback($form, $form_state) {
$commands[] = ajax_command_changed('#changed_div');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'changed' with asterisk marking inner div.
*/
function ajax_forms_test_advanced_commands_changed_asterisk_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_changed('#changed_div', '#changed_div_mark_this');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'css'.
*/
function ajax_forms_test_advanced_commands_css_callback($form, $form_state) {
$selector = '#css_div';
$color = 'blue';
$commands = array();
$commands[] = ajax_command_css($selector, array('background-color' => $color));
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'data'.
*/
function ajax_forms_test_advanced_commands_data_callback($form, $form_state) {
$selector = '#data_div';
$commands = array();
$commands[] = ajax_command_data($selector, 'testkey', 'testvalue');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'invoke'.
*/
function ajax_forms_test_advanced_commands_invoke_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_invoke('#invoke_div', 'addClass', array('error'));
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'html'.
*/
function ajax_forms_test_advanced_commands_html_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_html('#html_div', 'replacement text');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'insert'.
*/
function ajax_forms_test_advanced_commands_insert_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_insert('#insert_div', 'insert replacement text');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'prepend'.
*/
function ajax_forms_test_advanced_commands_prepend_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_prepend('#prepend_div', "prepended text");
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'remove'.
*/
function ajax_forms_test_advanced_commands_remove_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_remove('#remove_text');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'restripe'.
*/
function ajax_forms_test_advanced_commands_restripe_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_restripe('#restripe_table');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'settings'.
*/
function ajax_forms_test_advanced_commands_settings_callback($form, $form_state) {
$commands = array();
$setting['ajax_forms_test']['foo'] = 42;
$commands[] = ajax_command_settings($setting);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* Ajax callback for 'add_css'.
*/
function ajax_forms_test_advanced_commands_add_css_callback($form, $form_state) {
$commands = array();
$commands[] = ajax_command_add_css('my/file.css');
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
* This form and its related submit and callback functions demonstrate
* not validating another form element when a single Ajax element is triggered.
*
* The "drivertext" element is an Ajax-enabled textfield, free-form.
* The "required_field" element is a textfield marked required.
*
* The correct behavior is that the Ajax-enabled drivertext element should
* be able to trigger without causing validation of the "required_field".
*/
function ajax_forms_test_validation_form($form, &$form_state) {
$form['drivertext'] = array(
'#title' => t('AJAX-enabled textfield.'),
'#description' => t("When this one AJAX-triggers and the spare required field is empty, you should not get an error."),
'#type' => 'textfield',
'#default_value' => !empty($form_state['values']['drivertext']) ? $form_state['values']['drivertext'] : "",
'#ajax' => array(
'callback' => 'ajax_forms_test_validation_form_callback',
'wrapper' => 'message_area',
'method' => 'replace',
),
'#suffix' => '<div id="message_area"></div>',
);
$form['spare_required_field'] = array(
'#title' => t("Spare Required Field"),
'#type' => 'textfield',
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Submit handler for the validation form.
*/
function ajax_forms_test_validation_form_submit($form, $form_state) {
drupal_set_message(t("Validation form submitted"));
}
/**
* Ajax callback for the 'drivertext' element of the validation form.
*/
function ajax_forms_test_validation_form_callback($form, $form_state) {
drupal_set_message("ajax_forms_test_validation_form_callback invoked");
drupal_set_message(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", array('%drivertext' => $form_state['values']['drivertext'], '%spare_required_field' => $form_state['values']['spare_required_field'])));
return '<div id="message_area">ajax_forms_test_validation_form_callback at ' . date('c') . '</div>';
}
/**
* Form builder: Builds a form that triggers a simple AJAX callback.
*/
function ajax_forms_test_lazy_load_form($form, &$form_state) {
$form['add_files'] = array(
'#type' => 'checkbox',
'#default_value' => FALSE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#ajax' => array(
'callback' => 'ajax_forms_test_lazy_load_form_ajax',
),
);
return $form;
}
/**
* Form submit handler: Adds JavaScript and CSS that wasn't on the original form.
*/
function ajax_forms_test_lazy_load_form_submit($form, &$form_state) {
if ($form_state['values']['add_files']) {
drupal_add_js(array('ajax_forms_test_lazy_load_form_submit' => 'executed'), 'setting');
drupal_add_css(drupal_get_path('module', 'system') . '/system.admin.css');
drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
}
$form_state['rebuild'] = TRUE;
}
/**
* AJAX callback for the ajax_forms_test_lazy_load_form() form.
*
* This function returns nothing, because all we're interested in testing is
* ajax_render() adding commands for JavaScript and CSS added during the page
* request, such as the ones added in ajax_forms_test_lazy_load_form_submit().
*/
function ajax_forms_test_lazy_load_form_ajax($form, &$form_state) {
return NULL;
}

View file

@ -0,0 +1,11 @@
name = AJAX Test
description = Support module for AJAX framework tests.
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,79 @@
<?php
/**
* @file
* Helper module for Ajax framework tests.
*/
/**
* Implements hook_menu().
*/
function ajax_test_menu() {
$items['ajax-test/render'] = array(
'title' => 'ajax_render',
'page callback' => 'ajax_test_render',
'delivery callback' => 'ajax_deliver',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ajax-test/render-error'] = array(
'title' => 'ajax_render_error',
'page callback' => 'ajax_test_error',
'delivery callback' => 'ajax_deliver',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['ajax-test/link'] = array(
'title' => 'AJAX Link',
'page callback' => 'ajax_test_link',
'access callback' => TRUE,
);
return $items;
}
/**
* Implements hook_system_theme_info().
*/
function ajax_test_system_theme_info() {
$themes['test_theme'] = drupal_get_path('module', 'ajax_test') . '/themes/test_theme/test_theme.info';
return $themes;
}
/**
* Menu callback; Return an element suitable for use by ajax_deliver().
*
* Additionally ensures that ajax_render() incorporates JavaScript settings
* generated during the page request by invoking drupal_add_js() with a dummy
* setting.
*/
function ajax_test_render() {
drupal_add_js(array('ajax' => 'test'), 'setting');
return array('#type' => 'ajax', '#commands' => array());
}
/**
* Menu callback; Returns Ajax element with #error property set.
*/
function ajax_test_error() {
$message = '';
if (!empty($_GET['message'])) {
$message = $_GET['message'];
}
return array('#type' => 'ajax', '#error' => $message);
}
/**
* Menu callback; Renders a #type link with #ajax.
*/
function ajax_test_link() {
$build['link'] = array(
'#type' => 'link',
'#title' => 'Show help',
'#href' => 'filter/tips',
'#ajax' => array(
'wrapper' => 'block-system-main',
),
);
return $build;
}

View file

@ -0,0 +1,403 @@
<?php
/**
* @file
* Tests for the Batch API.
*/
/**
* Tests for the Batch API.
*/
class BatchProcessingTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Batch processing',
'description' => 'Test batch processing in form and non-form workflow.',
'group' => 'Batch API',
);
}
function setUp() {
parent::setUp('batch_test');
}
/**
* Test batches triggered outside of form submission.
*/
function testBatchNoForm() {
// Displaying the page triggers batch 1.
$this->drupalGet('batch-test/no-form');
$this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in a form submit handler.
*/
function testBatchForm() {
// Batch 0: no operation.
$edit = array('batch' => 'batch_0');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 1: several simple operations.
$edit = array('batch' => 'batch_1');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 2: one multistep operation.
$edit = array('batch' => 'batch_2');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 3: simple + multistep combined.
$edit = array('batch' => 'batch_3');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
// Batch 4: nested batch.
$edit = array('batch' => 'batch_4');
$this->drupalPost('batch-test/simple', $edit, 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in a multistep form.
*/
function testBatchFormMultistep() {
$this->drupalGet('batch-test/multistep');
$this->assertText('step 1', t('Form is displayed in step 1.'));
// First step triggers batch 1.
$this->drupalPost(NULL, array(), 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
$this->assertText('step 2', t('Form is displayed in step 2.'));
// Second step triggers batch 2.
$this->drupalPost(NULL, array(), 'Submit');
$this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in different submit handlers on the same form.
*/
function testBatchFormMultipleBatches() {
// Batches 1, 2 and 3 are triggered in sequence by different submit
// handlers. Each submit handler modify the submitted 'value'.
$value = rand(0, 255);
$edit = array('value' => $value);
$this->drupalPost('batch-test/chained', $edit, 'Submit');
// Check that result messages are present and in the correct order.
$this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
// The stack contains execution order of batch callbacks and submit
// hanlders and logging of corresponding $form_state[{values'].
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Test batches defined in a programmatically submitted form.
*
* Same as above, but the form is submitted through drupal_form_execute().
*/
function testBatchFormProgrammatic() {
// Batches 1, 2 and 3 are triggered in sequence by different submit
// handlers. Each submit handler modify the submitted 'value'.
$value = rand(0, 255);
$this->drupalGet('batch-test/programmatic/' . $value);
// Check that result messages are present and in the correct order.
$this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
// The stack contains execution order of batch callbacks and submit
// hanlders and logging of corresponding $form_state[{values'].
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
$this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.'));
}
/**
* Test that drupal_form_submit() can run within a batch operation.
*/
function testDrupalFormSubmitInBatch() {
// Displaying the page triggers a batch that programmatically submits a
// form.
$value = rand(0, 255);
$this->drupalGet('batch-test/nested-programmatic/' . $value);
$this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.'));
}
/**
* Test batches that return $context['finished'] > 1 do in fact complete.
* See http://drupal.org/node/600836
*/
function testBatchLargePercentage() {
// Displaying the page triggers batch 5.
$this->drupalGet('batch-test/large-percentage');
$this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_5'), t('Execution order was correct.'));
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
}
/**
* Will trigger a pass if the texts were found in order in the raw content.
*
* @param $texts
* Array of raw strings to look for .
* @param $message
* Message to display.
* @return
* TRUE on pass, FALSE on fail.
*/
function assertBatchMessages($texts, $message) {
$pattern = '|' . implode('.*', $texts) .'|s';
return $this->assertPattern($pattern, $message);
}
/**
* Helper function: return expected execution stacks for the test batches.
*/
function _resultStack($id, $value = 0) {
$stack = array();
switch ($id) {
case 'batch_1':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
break;
case 'batch_2':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 2 id $i";
}
break;
case 'batch_3':
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 1 id $i";
}
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 2 id $i";
}
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 2 id $i";
}
break;
case 'batch_4':
for ($i = 1; $i <= 5; $i++) {
$stack[] = "op 1 id $i";
}
$stack[] = 'setting up batch 2';
for ($i = 6; $i <= 10; $i++) {
$stack[] = "op 1 id $i";
}
$stack = array_merge($stack, $this->_resultStack('batch_2'));
break;
case 'batch_5':
for ($i = 1; $i <= 10; $i++) {
$stack[] = "op 5 id $i";
}
break;
case 'chained':
$stack[] = 'submit handler 1';
$stack[] = 'value = ' . $value;
$stack = array_merge($stack, $this->_resultStack('batch_1'));
$stack[] = 'submit handler 2';
$stack[] = 'value = ' . ($value + 1);
$stack = array_merge($stack, $this->_resultStack('batch_2'));
$stack[] = 'submit handler 3';
$stack[] = 'value = ' . ($value + 2);
$stack[] = 'submit handler 4';
$stack[] = 'value = ' . ($value + 3);
$stack = array_merge($stack, $this->_resultStack('batch_3'));
break;
}
return $stack;
}
/**
* Helper function: return expected result messages for the test batches.
*/
function _resultMessages($id) {
$messages = array();
switch ($id) {
case 'batch_0':
$messages[] = 'results for batch 0<br />none';
break;
case 'batch_1':
$messages[] = 'results for batch 1<br />op 1: processed 10 elements';
break;
case 'batch_2':
$messages[] = 'results for batch 2<br />op 2: processed 10 elements';
break;
case 'batch_3':
$messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements';
break;
case 'batch_4':
$messages[] = 'results for batch 4<br />op 1: processed 10 elements';
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
break;
case 'batch_5':
$messages[] = 'results for batch 5<br />op 1: processed 10 elements. $context[\'finished\'] > 1 returned from batch process, with success.';
break;
case 'chained':
$messages = array_merge($messages, $this->_resultMessages('batch_1'));
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
$messages = array_merge($messages, $this->_resultMessages('batch_3'));
break;
}
return $messages;
}
}
/**
* Tests for the Batch API Progress page.
*/
class BatchPageTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Batch progress page',
'description' => 'Test the content of the progress page.',
'group' => 'Batch API',
);
}
function setUp() {
parent::setUp('batch_test');
}
/**
* Tests that the batch API progress page uses the correct theme.
*/
function testBatchProgressPageTheme() {
// Make sure that the page which starts the batch (an administrative page)
// is using a different theme than would normally be used by the batch API.
variable_set('theme_default', 'bartik');
variable_set('admin_theme', 'seven');
// Log in as an administrator who can see the administrative theme.
$admin_user = $this->drupalCreateUser(array('view the administration theme'));
$this->drupalLogin($admin_user);
// Visit an administrative page that runs a test batch, and check that the
// theme that was used during batch execution (which the batch callback
// function saved as a variable) matches the theme used on the
// administrative page.
$this->drupalGet('admin/batch-test/test-theme');
// The stack should contain the name of the theme used on the progress
// page.
$this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.'));
}
}
/**
* Tests the function _batch_api_percentage() to make sure that the rounding
* works properly in all cases.
*/
class BatchPercentagesUnitTestCase extends DrupalUnitTestCase {
protected $testCases = array();
public static function getInfo() {
return array(
'name' => 'Batch percentages',
'description' => 'Unit tests of progress percentage rounding.',
'group' => 'Batch API',
);
}
function setUp() {
// Set up an array of test cases, where the expected values are the keys,
// and the values are arrays with the keys 'total' and 'current',
// corresponding with the function parameters of _batch_api_percentage().
$this->testCases = array(
// 1/2 is 50%.
'50' => array('total' => 2, 'current' => 1),
// Though we should never encounter a case where the current set is set
// 0, if we did, we should get 0%.
'0' => array('total' => 3, 'current' => 0),
// 1/3 is closer to 33% than to 34%.
'33' => array('total' => 3, 'current' => 1),
// 2/3 is closer to 67% than to 66%.
'67' => array('total' => 3, 'current' => 2),
// 1/199 should round up to 1%.
'1' => array('total' => 199, 'current' => 1),
// 198/199 should round down to 99%.
'99' => array('total' => 199, 'current' => 198),
// 199/200 would have rounded up to 100%, which would give the false
// impression of being finished, so we add another digit and should get
// 99.5%.
'99.5' => array('total' => 200, 'current' => 199),
// The same logic holds for 1/200: we should get 0.5%.
'0.5' => array('total' => 200, 'current' => 1),
// Numbers that come out evenly, such as 50/200, should be forced to have
// extra digits for consistancy.
'25.0' => array('total' => 200, 'current' => 50),
// Regardless of number of digits we're using, 100% should always just be
// 100%.
'100' => array('total' => 200, 'current' => 200),
// 1998/1999 should similarly round down to 99.9%.
'99.9' => array('total' => 1999, 'current' => 1998),
// 1999/2000 should add another digit and go to 99.95%.
'99.95' => array('total' => 2000, 'current' => 1999),
// 19999/20000 should add yet another digit and go to 99.995%.
'99.995' => array('total' => 20000, 'current' => 19999),
// The next five test cases simulate a batch with a single operation
// ('total' equals 1) that takes several steps to complete. Within the
// operation, we imagine that there are 501 items to process, and 100 are
// completed during each step. The percentages we get back should be
// rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
// but for the last pass through, when 500 out of 501 items have been
// processed, we do not want to round up to 100%, since that would
// erroneously indicate that the processing is complete.
'20' => array('total' => 1, 'current' => 100/501),
'40' => array('total' => 1, 'current' => 200/501),
'60' => array('total' => 1, 'current' => 300/501),
'80' => array('total' => 1, 'current' => 400/501),
'99.8' => array('total' => 1, 'current' => 500/501),
);
require_once DRUPAL_ROOT . '/includes/batch.inc';
parent::setUp();
}
/**
* Test the _batch_api_percentage() function.
*/
function testBatchPercentages() {
foreach ($this->testCases as $expected_result => $arguments) {
// PHP sometimes casts numeric strings that are array keys to integers,
// cast them back here.
$expected_result = (string) $expected_result;
$total = $arguments['total'];
$current = $arguments['current'];
$actual_result = _batch_api_percentage($total, $current);
if ($actual_result === $expected_result) {
$this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
}
else {
$this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
}
}
}
}

View file

@ -0,0 +1,163 @@
<?php
/**
* @file
* Batch callbacks for the Batch API tests.
*/
/**
* Implements callback_batch_operation().
*
* Simple batch operation.
*/
function _batch_test_callback_1($id, $sleep, &$context) {
// No-op, but ensure the batch take a couple iterations.
// Batch needs time to run for the test, so sleep a bit.
usleep($sleep);
// Track execution, and store some result for post-processing in the
// 'finished' callback.
batch_test_stack("op 1 id $id");
$context['results'][1][] = $id;
}
/**
* Implements callback_batch_operation().
*
* Multistep batch operation.
*/
function _batch_test_callback_2($start, $total, $sleep, &$context) {
// Initialize context with progress information.
if (!isset($context['sandbox']['current'])) {
$context['sandbox']['current'] = $start;
$context['sandbox']['count'] = 0;
}
// Process by groups of 5 (arbitrary value).
$limit = 5;
for ($i = 0; $i < $limit && $context['sandbox']['count'] < $total; $i++) {
// No-op, but ensure the batch take a couple iterations.
// Batch needs time to run for the test, so sleep a bit.
usleep($sleep);
// Track execution, and store some result for post-processing in the
// 'finished' callback.
$id = $context['sandbox']['current'] + $i;
batch_test_stack("op 2 id $id");
$context['results'][2][] = $id;
// Update progress information.
$context['sandbox']['count']++;
}
$context['sandbox']['current'] += $i;
// Inform batch engine about progress.
if ($context['sandbox']['count'] != $total) {
$context['finished'] = $context['sandbox']['count'] / $total;
}
}
/**
* Implements callback_batch_operation().
*
* Simple batch operation.
*/
function _batch_test_callback_5($id, $sleep, &$context) {
// No-op, but ensure the batch take a couple iterations.
// Batch needs time to run for the test, so sleep a bit.
usleep($sleep);
// Track execution, and store some result for post-processing in the
// 'finished' callback.
batch_test_stack("op 5 id $id");
$context['results'][5][] = $id;
// This test is to test finished > 1
$context['finished'] = 3.14;
}
/**
* Implements callback_batch_operation().
*
* Batch operation setting up its own batch.
*/
function _batch_test_nested_batch_callback() {
batch_test_stack('setting up batch 2');
batch_set(_batch_test_batch_2());
}
/**
* Implements callback_batch_finished().
*
* Common 'finished' callbacks for batches 1 to 4.
*/
function _batch_test_finished_helper($batch_id, $success, $results, $operations) {
$messages = array("results for batch $batch_id");
if ($results) {
foreach ($results as $op => $op_results) {
$messages[] = 'op '. $op . ': processed ' . count($op_results) . ' elements';
}
}
else {
$messages[] = 'none';
}
if (!$success) {
// A fatal error occurred during the processing.
$error_operation = reset($operations);
$messages[] = t('An error occurred while processing @op with arguments:<br/>@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE)));
}
drupal_set_message(implode('<br />', $messages));
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 0.
*/
function _batch_test_finished_0($success, $results, $operations) {
_batch_test_finished_helper(0, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 1.
*/
function _batch_test_finished_1($success, $results, $operations) {
_batch_test_finished_helper(1, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 2.
*/
function _batch_test_finished_2($success, $results, $operations) {
_batch_test_finished_helper(2, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 3.
*/
function _batch_test_finished_3($success, $results, $operations) {
_batch_test_finished_helper(3, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 4.
*/
function _batch_test_finished_4($success, $results, $operations) {
_batch_test_finished_helper(4, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* 'finished' callback for batch 5.
*/
function _batch_test_finished_5($success, $results, $operations) {
_batch_test_finished_helper(5, $success, $results, $operations);
}

View file

@ -0,0 +1,11 @@
name = "Batch API test"
description = "Support module for Batch API tests."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,513 @@
<?php
/**
* @file
* Helper module for the Batch API tests.
*/
/**
* Implement hook_menu().
*/
function batch_test_menu() {
$items = array();
$items['batch-test'] = array(
'title' => 'Batch test',
'page callback' => 'drupal_get_form',
'page arguments' => array('batch_test_simple_form'),
'access callback' => TRUE,
);
// Simple form: one submit handler, setting a batch.
$items['batch-test/simple'] = array(
'title' => 'Simple',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
// Multistep form: two steps, each setting a batch.
$items['batch-test/multistep'] = array(
'title' => 'Multistep',
'page callback' => 'drupal_get_form',
'page arguments' => array('batch_test_multistep_form'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
// Chained form: four submit handlers, several of which set a batch.
$items['batch-test/chained'] = array(
'title' => 'Chained',
'page callback' => 'drupal_get_form',
'page arguments' => array('batch_test_chained_form'),
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);
// Programmatic form: the page submits the 'Chained' form through
// drupal_form_submit().
$items['batch-test/programmatic'] = array(
'title' => 'Programmatic',
'page callback' => 'batch_test_programmatic',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
// No form: fire a batch simply by accessing a page.
$items['batch-test/no-form'] = array(
'title' => 'Simple page',
'page callback' => 'batch_test_no_form',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 4,
);
// No form: fire a batch; return > 100% complete
$items['batch-test/large-percentage'] = array(
'title' => 'Simple page with batch over 100% complete',
'page callback' => 'batch_test_large_percentage',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 5,
);
// Tests programmatic form submission within a batch operation.
$items['batch-test/nested-programmatic'] = array(
'title' => 'Nested programmatic',
'page callback' => 'batch_test_nested_drupal_form_submit',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 6,
);
// Landing page to test redirects.
$items['batch-test/redirect'] = array(
'title' => 'Redirect',
'page callback' => 'batch_test_redirect_page',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
'weight' => 7,
);
// This item lives under 'admin' so that the page uses the admin theme.
$items['admin/batch-test/test-theme'] = array(
'page callback' => 'batch_test_theme_batch',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Simple form.
*/
function batch_test_simple_form() {
$form['batch'] = array(
'#type' => 'select',
'#title' => 'Choose batch',
'#options' => array(
'batch_0' => 'batch 0',
'batch_1' => 'batch 1',
'batch_2' => 'batch 2',
'batch_3' => 'batch 3',
'batch_4' => 'batch 4',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Submit handler for the simple form.
*/
function batch_test_simple_form_submit($form, &$form_state) {
batch_test_stack(NULL, TRUE);
$function = '_batch_test_' . $form_state['values']['batch'];
batch_set($function());
$form_state['redirect'] = 'batch-test/redirect';
}
/**
* Multistep form.
*/
function batch_test_multistep_form($form, &$form_state) {
if (empty($form_state['storage']['step'])) {
$form_state['storage']['step'] = 1;
}
$form['step_display'] = array(
'#markup' => 'step ' . $form_state['storage']['step'] . '<br/>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}
/**
* Submit handler for the multistep form.
*/
function batch_test_multistep_form_submit($form, &$form_state) {
batch_test_stack(NULL, TRUE);
switch ($form_state['storage']['step']) {
case 1:
batch_set(_batch_test_batch_1());
break;
case 2:
batch_set(_batch_test_batch_2());
break;
}
if ($form_state['storage']['step'] < 2) {
$form_state['storage']['step']++;
$form_state['rebuild'] = TRUE;
}
// This will only be effective on the last step.
$form_state['redirect'] = 'batch-test/redirect';
}
/**
* Form with chained submit callbacks.
*/
function batch_test_chained_form() {
// This value is used to test that $form_state persists through batched
// submit handlers.
$form['value'] = array(
'#type' => 'textfield',
'#title' => 'Value',
'#default_value' => 1,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
$form['#submit'] = array(
'batch_test_chained_form_submit_1',
'batch_test_chained_form_submit_2',
'batch_test_chained_form_submit_3',
'batch_test_chained_form_submit_4',
);
return $form;
}
/**
* Submit handler #1 for the chained form.
*/
function batch_test_chained_form_submit_1($form, &$form_state) {
batch_test_stack(NULL, TRUE);
batch_test_stack('submit handler 1');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
batch_set(_batch_test_batch_1());
// This redirect should not be taken into account.
$form_state['redirect'] = 'should/be/discarded';
}
/**
* Submit handler #2 for the chained form.
*/
function batch_test_chained_form_submit_2($form, &$form_state) {
batch_test_stack('submit handler 2');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
batch_set(_batch_test_batch_2());
// This redirect should not be taken into account.
$form_state['redirect'] = 'should/be/discarded';
}
/**
* Submit handler #3 for the chained form.
*/
function batch_test_chained_form_submit_3($form, &$form_state) {
batch_test_stack('submit handler 3');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
// This redirect should not be taken into account.
$form_state['redirect'] = 'should/be/discarded';
}
/**
* Submit handler #4 for the chained form.
*/
function batch_test_chained_form_submit_4($form, &$form_state) {
batch_test_stack('submit handler 4');
batch_test_stack('value = ' . $form_state['values']['value']);
$form_state['values']['value']++;
batch_set(_batch_test_batch_3());
// This is the redirect that should prevail.
$form_state['redirect'] = 'batch-test/redirect';
}
/**
* Menu callback: programmatically submits the 'Chained' form.
*/
function batch_test_programmatic($value = 1) {
$form_state = array(
'values' => array('value' => $value)
);
drupal_form_submit('batch_test_chained_form', $form_state);
return 'Got out of a programmatic batched form.';
}
/**
* Menu callback: programmatically submits a form within a batch.
*/
function batch_test_nested_drupal_form_submit($value = 1) {
// Set the batch and process it.
$batch['operations'] = array(
array('_batch_test_nested_drupal_form_submit_callback', array($value)),
);
batch_set($batch);
batch_process('batch-test/redirect');
}
/**
* Batch operation: submits form_test_mock_form using drupal_form_submit().
*/
function _batch_test_nested_drupal_form_submit_callback($value) {
$state['values']['test_value'] = $value;
drupal_form_submit('batch_test_mock_form', $state);
}
/**
* A simple form with a textfield and submit button.
*/
function batch_test_mock_form($form, $form_state) {
$form['test_value'] = array(
'#type' => 'textfield',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Submit handler for the batch_test_mock form.
*/
function batch_test_mock_form_submit($form, &$form_state) {
batch_test_stack('mock form submitted with value = ' . $form_state['values']['test_value']);
}
/**
* Menu callback: fire a batch process without a form submission.
*/
function batch_test_no_form() {
batch_test_stack(NULL, TRUE);
batch_set(_batch_test_batch_1());
batch_process('batch-test/redirect');
}
/**
* Menu callback: fire a batch process without a form submission.
*/
function batch_test_large_percentage() {
batch_test_stack(NULL, TRUE);
batch_set(_batch_test_batch_5());
batch_process('batch-test/redirect');
}
/**
* Menu callback: successful redirection.
*/
function batch_test_redirect_page() {
return 'Redirection successful.';
}
/**
* Batch 0: no operation.
*/
function _batch_test_batch_0() {
$batch = array(
'operations' => array(),
'finished' => '_batch_test_finished_0',
'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 1: repeats a simple operation.
*
* Operations: op 1 from 1 to 10.
*/
function _batch_test_batch_1() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_1',
'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 2: single multistep operation.
*
* Operations: op 2 from 1 to 10.
*/
function _batch_test_batch_2() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array(
array('_batch_test_callback_2', array(1, $total, $sleep)),
);
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_2',
'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 3: both single and multistep operations.
*
* Operations:
* - op 1 from 1 to 5,
* - op 2 from 1 to 5,
* - op 1 from 6 to 10,
* - op 2 from 6 to 10.
*/
function _batch_test_batch_3() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= round($total / 2); $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$operations[] = array('_batch_test_callback_2', array(1, $total / 2, $sleep));
for ($i = round($total / 2) + 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$operations[] = array('_batch_test_callback_2', array(6, $total / 2, $sleep));
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_3',
'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 4: batch within a batch.
*
* Operations:
* - op 1 from 1 to 5,
* - set batch 2 (op 2 from 1 to 10, should run at the end)
* - op 1 from 6 to 10,
*/
function _batch_test_batch_4() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= round($total / 2); $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$operations[] = array('_batch_test_nested_batch_callback', array());
for ($i = round($total / 2) + 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_1', array($i, $sleep));
}
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_4',
'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Batch 5: repeats a simple operation.
*
* Operations: op 1 from 1 to 10.
*/
function _batch_test_batch_5() {
// Ensure the batch takes at least two iterations.
$total = 10;
$sleep = (1000000 / $total) * 2;
$operations = array();
for ($i = 1; $i <= $total; $i++) {
$operations[] = array('_batch_test_callback_5', array($i, $sleep));
}
$batch = array(
'operations' => $operations,
'finished' => '_batch_test_finished_5',
'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
);
return $batch;
}
/**
* Menu callback: run a batch for testing theme used on the progress page.
*/
function batch_test_theme_batch() {
batch_test_stack(NULL, TRUE);
$batch = array(
'operations' => array(
array('_batch_test_theme_callback', array()),
),
);
batch_set($batch);
batch_process('batch-test/redirect');
}
/**
* Batch callback function for testing the theme used on the progress page.
*/
function _batch_test_theme_callback() {
// Because drupalGet() steps through the full progressive batch before
// returning control to the test function, we cannot test that the correct
// theme is being used on the batch processing page by viewing that page
// directly. Instead, we save the theme being used in a variable here, so
// that it can be loaded and inspected in the thread running the test.
global $theme;
batch_test_stack($theme);
}
/**
* Helper function: store or retrieve traced execution data.
*/
function batch_test_stack($data = NULL, $reset = FALSE) {
if ($reset) {
variable_del('batch_test_stack');
}
if (!isset($data)) {
return variable_get('batch_test_stack', array());
}
$stack = variable_get('batch_test_stack', array());
$stack[] = $data;
variable_set('batch_test_stack', $stack);
}

View file

@ -0,0 +1,38 @@
<?php
/**
* Perform early bootstrap tests.
*/
class EarlyBootstrapTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Early bootstrap test',
'description' => 'Confirm that calling module_implements() during early bootstrap does not pollute the module_implements() cache.',
'group' => 'System',
);
}
function setUp() {
parent::setUp('boot_test_1', 'boot_test_2');
}
/**
* Test hook_boot() on both regular and "early exit" pages.
*/
public function testHookBoot() {
$paths = array('', 'early_exit');
foreach ($paths as $path) {
// Empty the module_implements() caches.
module_implements(NULL, FALSE, TRUE);
// Do a request to the front page, which will call module_implements()
// during hook_boot().
$this->drupalGet($path);
// Reset the static cache so we get implementation data from the persistent
// cache.
drupal_static_reset();
// Make sure we get a full list of all modules implementing hook_help().
$modules = module_implements('help');
$this->assertTrue(in_array('boot_test_2', $modules));
}
}
}

View file

@ -0,0 +1,11 @@
name = Early bootstrap tests
description = A support module for hook_boot testing.
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,21 @@
<?php
/**
* @file
* Tests calling module_implements() during hook_boot() invocation.
*/
/**
* Implements hook_boot().
*/
function boot_test_1_boot() {
// Calling module_implements during hook_boot() will return "vital" modules
// only, and this list of modules will be statically cached.
module_implements('help');
// Define a special path to test that the static cache isn't written away
// if we exit before having completed the bootstrap.
if ($_GET['q'] == 'early_exit') {
module_implements_write_cache();
exit();
}
}

View file

@ -0,0 +1,11 @@
name = Early bootstrap tests
description = A support module for hook_boot hook testing.
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,13 @@
<?php
/**
* @file
* Defines a hook_help() implementation in a non-"bootstrap" module.
*/
/**
* Implements hook_help().
*/
function boot_test_2_help($path, $arg) {
// Empty hook.
}

View file

@ -0,0 +1,965 @@
<?php
class BootstrapIPAddressTestCase extends DrupalWebTestCase {
protected $oldserver;
protected $remote_ip;
protected $proxy_ip;
protected $proxy2_ip;
protected $forwarded_ip;
protected $cluster_ip;
protected $untrusted_ip;
public static function getInfo() {
return array(
'name' => 'IP address and HTTP_HOST test',
'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.',
'group' => 'Bootstrap'
);
}
function setUp() {
$this->oldserver = $_SERVER;
$this->remote_ip = '127.0.0.1';
$this->proxy_ip = '127.0.0.2';
$this->proxy2_ip = '127.0.0.3';
$this->forwarded_ip = '127.0.0.4';
$this->cluster_ip = '127.0.0.5';
$this->untrusted_ip = '0.0.0.0';
drupal_static_reset('ip_address');
$_SERVER['REMOTE_ADDR'] = $this->remote_ip;
unset($_SERVER['HTTP_X_FORWARDED_FOR']);
unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']);
parent::setUp();
}
function tearDown() {
$_SERVER = $this->oldserver;
drupal_static_reset('ip_address');
parent::tearDown();
}
/**
* test IP Address and hostname
*/
function testIPAddressHost() {
// Test the normal IP address.
$this->assertTrue(
ip_address() == $this->remote_ip,
'Got remote IP address.'
);
// Proxy forwarding on but no proxy addresses defined.
variable_set('reverse_proxy', 1);
$this->assertTrue(
ip_address() == $this->remote_ip,
'Proxy forwarding without trusted proxies got remote IP address.'
);
// Proxy forwarding on and proxy address not trusted.
variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip));
drupal_static_reset('ip_address');
$_SERVER['REMOTE_ADDR'] = $this->untrusted_ip;
$this->assertTrue(
ip_address() == $this->untrusted_ip,
'Proxy forwarding with untrusted proxy got remote IP address.'
);
// Proxy forwarding on and proxy address trusted.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip;
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->forwarded_ip,
'Proxy forwarding with trusted proxy got forwarded IP address.'
);
// Proxy forwarding on and proxy address trusted and visiting from proxy.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = $this->proxy_ip;
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->proxy_ip,
'Visiting from trusted proxy got proxy IP address.'
);
// Multi-tier architecture with comma separated values in header.
$_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
$_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->forwarded_ip,
'Proxy forwarding with trusted 2-tier proxy got forwarded IP address.'
);
// Custom client-IP header.
variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP');
$_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip;
drupal_static_reset('ip_address');
$this->assertTrue(
ip_address() == $this->cluster_ip,
'Cluster environment got cluster client IP.'
);
// Verifies that drupal_valid_http_host() prevents invalid characters.
$this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), 'HTTP_HOST with / is invalid');
$this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), 'HTTP_HOST with \\ is invalid');
$this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), 'HTTP_HOST with &lt; is invalid');
$this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), 'HTTP_HOST with .. is invalid');
// Verifies that host names are shorter than 1000 characters.
$this->assertFalse(drupal_valid_http_host(str_repeat('x', 1001)), 'HTTP_HOST with more than 1000 characters is invalid.');
$this->assertFalse(drupal_valid_http_host(str_repeat('.', 101)), 'HTTP_HOST with more than 100 subdomains is invalid.');
$this->assertFalse(drupal_valid_http_host(str_repeat(':', 101)), 'HTTP_HOST with more than 100 portseparators is invalid.');
// IPv6 loopback address
$this->assertTrue(drupal_valid_http_host('[::1]:80'), 'HTTP_HOST containing IPv6 loopback is valid');
}
}
class BootstrapPageCacheTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Page cache test',
'description' => 'Enable the page cache and test it with various HTTP requests.',
'group' => 'Bootstrap'
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* Test support for requests containing If-Modified-Since and If-None-Match headers.
*/
function testConditionalRequests() {
variable_set('cache', 1);
// Fill the cache.
$this->drupalGet('');
$this->drupalHead('');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$etag = $this->drupalGetHeader('ETag');
$last_modified = $this->drupalGetHeader('Last-Modified');
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
$this->assertResponse(304, 'Conditional request returned 304 Not Modified.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
$this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
$this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
$this->assertResponse(200, 'Conditional request without If-None-Match returned 200 OK.');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC7231, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
$this->assertResponse(200, 'Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
$this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.');
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absence of Page was not cached.');
$this->assertFalse($this->drupalGetHeader('ETag'), 'ETag HTTP headers are not present for logged in users.');
$this->assertFalse($this->drupalGetHeader('Last-Modified'), 'Last-Modified HTTP headers are not present for logged in users.');
}
/**
* Test cache headers.
*/
function testPageCache() {
variable_set('cache', 1);
// Fill the cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary header was sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
// Check cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary: Cookie header was sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
$this->assertEqual($this->drupalGetHeader('X-Content-Type-Options'), 'nosniff', 'X-Content-Type-Options header was sent.');
// Check replacing default headers.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
$this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', 'Default header was replaced.');
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
$this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', 'Default header was replaced.');
// Check that authenticated users bypass the cache.
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
$this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
}
/**
* Test page compression.
*
* The test should pass even if zlib.output_compression is enabled in php.ini,
* .htaccess or similar, or if compression is done outside PHP, e.g. by the
* mod_deflate Apache module.
*/
function testPageCompression() {
variable_set('cache', 1);
// Fill the cache and verify that output is compressed.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
$this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
$this->assertRaw('</html>', 'Page was gzip compressed.');
// Verify that cached output is compressed.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', 'A Content-Encoding header was sent.');
$this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
$this->assertRaw('</html>', 'Page was gzip compressed.');
// Verify that a client without compression support gets an uncompressed page.
$this->drupalGet('');
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
$this->assertFalse($this->drupalGetHeader('Content-Encoding'), 'A Content-Encoding header was not sent.');
$this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), 'Site title matches.');
$this->assertRaw('</html>', 'Page was not compressed.');
// Verify that an empty page doesn't throw an error when being decompressed.
$this->drupalGet('system-test/empty-page');
// Disable compression mode.
variable_set('page_compression', FALSE);
// Verify if cached page is still available for a client with compression support.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
$this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
$this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support enabled).');
// Verify if cached page is still available for a client without compression support.
$this->drupalGet('');
$this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support disabled).');
}
/**
* Test page cache headers.
*/
function testPageCacheHeaders() {
variable_set('cache', 1);
// First request should store a response in the page cache.
$this->drupalGet('system-test/page-cache-headers');
// The test callback should remove the query string leaving the same path
// as the previous request, which we'll try to retrieve from cache_page.
$this->drupalGet('system-test/page-cache-headers', array('query' => array('return_headers' => 'TRUE')));
$headers = json_decode($this->drupalGetHeader('Page-Cache-Headers'), TRUE);
if (is_null($headers)) {
$this->fail('No headers were retrieved from the page cache.');
}
else {
$this->assertEqual($headers['X-Content-Type-Options'], 'nosniff', 'X-Content-Type-Options header retrieved from response in the page cache.');
}
}
}
class BootstrapVariableTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp('system_test');
}
public static function getInfo() {
return array(
'name' => 'Variable test',
'description' => 'Make sure the variable system functions correctly.',
'group' => 'Bootstrap'
);
}
/**
* testVariable
*/
function testVariable() {
// Setting and retrieving values.
$variable = $this->randomName();
variable_set('simpletest_bootstrap_variable_test', $variable);
$this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), 'Setting and retrieving values');
// Make sure the variable persists across multiple requests.
$this->drupalGet('system-test/variable-get');
$this->assertText($variable, 'Variable persists across multiple requests');
// Deleting variables.
$default_value = $this->randomName();
variable_del('simpletest_bootstrap_variable_test');
$variable = variable_get('simpletest_bootstrap_variable_test', $default_value);
$this->assertIdentical($variable, $default_value, 'Deleting variables');
}
/**
* Makes sure that the default variable parameter is passed through okay.
*/
function testVariableDefaults() {
// Tests passing nothing through to the default.
$this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), 'Variables are correctly defaulting to NULL.');
// Tests passing 5 to the default parameter.
$this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), 'The default variable parameter is passed through correctly.');
}
}
/**
* Tests the auto-loading behavior of the code registry.
*/
class BootstrapAutoloadTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Code registry',
'description' => 'Test that the code registry functions correctly.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('drupal_autoload_test');
}
/**
* Tests that autoloader name matching is not case sensitive.
*/
function testAutoloadCase() {
// Test interface autoloader.
$this->assertTrue(drupal_autoload_interface('drupalautoloadtestinterface'), 'drupal_autoload_interface() recognizes <em>DrupalAutoloadTestInterface</em> in lower case.');
// Test class autoloader.
$this->assertTrue(drupal_autoload_class('drupalautoloadtestclass'), 'drupal_autoload_class() recognizes <em>DrupalAutoloadTestClass</em> in lower case.');
// Test trait autoloader.
if (version_compare(PHP_VERSION, '5.4') >= 0) {
$this->assertTrue(drupal_autoload_trait('drupalautoloadtesttrait'), 'drupal_autoload_trait() recognizes <em>DrupalAutoloadTestTrait</em> in lower case.');
}
}
}
/**
* Test hook_boot() and hook_exit().
*/
class HookBootExitTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Boot and exit hook invocation',
'description' => 'Test that hook_boot() and hook_exit() are called correctly.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('system_test', 'dblog');
}
/**
* Test calling of hook_boot() and hook_exit().
*/
function testHookBootExit() {
// Test with cache disabled. Boot and exit should always fire.
variable_set('cache', 0);
$this->drupalGet('');
$calls = 1;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));
// Test with normal cache. Boot and exit should be called.
variable_set('cache', 1);
$this->drupalGet('');
$calls++;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.'));
// Boot and exit should not fire since the page is cached.
variable_set('page_cache_invoke_hooks', FALSE);
$this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
$this->drupalGet('');
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with aggressive cache and a cached page.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with aggressive cache and a cached page.'));
// Test with page cache cleared, boot and exit should be called.
$this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
$this->drupalGet('');
$calls++;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with aggressive cache and no cached page.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with aggressive cache and no cached page.'));
}
}
/**
* Test drupal_get_filename()'s availability.
*/
class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Get filename test (without the system table)',
'description' => 'Test that drupal_get_filename() works correctly when the database is not available.',
'group' => 'Bootstrap',
);
}
/**
* The last file-related error message triggered by the filename test.
*
* Used by BootstrapGetFilenameTestCase::testDrupalGetFilename().
*/
protected $getFilenameTestTriggeredError;
/**
* Test that drupal_get_filename() works correctly when the file is not found in the database.
*/
function testDrupalGetFilename() {
// Reset the static cache so we can test the "db is not active" code of
// drupal_get_filename().
drupal_static_reset('drupal_get_filename');
// Retrieving the location of a module.
$this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.'));
// Retrieving the location of a theme.
$this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.'));
// Retrieving the location of a theme engine.
$this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.'));
// Retrieving the location of a profile. Profiles are a special case with
// a fixed location and naming.
$this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.'));
// When a file is not found in the database cache, drupal_get_filename()
// searches several locations on the filesystem, including the DRUPAL_ROOT
// directory. We use the '.script' extension below because this is a
// non-existent filetype that will definitely not exist in the database.
// Since there is already a scripts directory, drupal_get_filename() will
// automatically check there for 'script' files, just as it does for (e.g.)
// 'module' files in modules.
$this->assertIdentical(drupal_get_filename('script', 'test'), 'scripts/test.script', t('Retrieve test script location.'));
// When searching for a module that does not exist, drupal_get_filename()
// should return NULL and trigger an appropriate error message.
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$non_existing_module = $this->randomName();
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for an item that does not exist triggers the correct error.');
restore_error_handler();
// Check that the result is stored in the file system scan cache.
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
// Performing the search again in the same request still should not find
// the file, but the error message should not be repeated (therefore we do
// not override the error handler here).
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL during the second search.');
}
/**
* Skips handling of "file not found" errors.
*/
public function fileNotFoundErrorHandler($error_level, $message, $filename, $line) {
// Skip error handling if this is a "file not found" error.
if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
$this->getFilenameTestTriggeredError = $message;
return;
}
_drupal_error_handler($error_level, $message, $filename, $line);
}
}
/**
* Test drupal_get_filename() in the context of a full Drupal installation.
*/
class BootstrapGetFilenameWebTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Get filename test (full installation)',
'description' => 'Test that drupal_get_filename() works correctly in the context of a full Drupal installation.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* The last file-related error message triggered by the filename test.
*
* Used by BootstrapGetFilenameWebTestCase::testDrupalGetFilename().
*/
protected $getFilenameTestTriggeredError;
/**
* Test that drupal_get_filename() works correctly with a full Drupal site.
*/
function testDrupalGetFilename() {
// Search for a module that exists in the file system and the {system}
// table and make sure that it is found.
$this->assertIdentical(drupal_get_filename('module', 'node'), 'modules/node/node.module', 'Module found at expected location.');
// Search for a module that does not exist in either the file system or the
// {system} table. Make sure that an appropriate error is triggered and
// that the module winds up in the static and persistent cache.
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$non_existing_module = $this->randomName();
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that does not exist triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files persistent cache.');
// Simulate moving a module to a location that does not match the location
// in the {system} table and perform similar tests as above.
db_update('system')
->fields(array('filename' => 'modules/simpletest/tests/fake_location/module_test.module'))
->condition('name', 'module_test')
->condition('type', 'module')
->execute();
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$this->assertIdentical(drupal_get_filename('module', 'module_test'), 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved finds the module at its new location.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'module_test'))) === 0, 'Searching for a module that has moved triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files persistent cache.');
// Simulate a module that exists in the {system} table but does not exist
// in the file system and perform similar tests as above.
$non_existing_module = $this->randomName();
db_update('system')
->fields(array('name' => $non_existing_module))
->condition('name', 'module_test')
->condition('type', 'module')
->execute();
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that exists in the system table but not in the file system returns NULL.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that exists in the system table but not in the file system triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files persistent cache.');
// Simulate a module that exists in the file system but not in the {system}
// table and perform similar tests as above.
db_delete('system')
->condition('name', 'common_test')
->condition('type', 'module')
->execute();
system_list_reset();
$this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$this->assertIdentical(drupal_get_filename('module', 'common_test'), 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table finds the module at its actual location.');
$this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'common_test'))) === 0, 'Searching for a module that does not exist in the system table triggers the correct error.');
restore_error_handler();
$file_scans = _drupal_file_scan_cache();
$this->assertIdentical($file_scans['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
$cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
$this->assertIdentical($cache->data['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files persistent cache.');
}
/**
* Skips handling of "file not found" errors.
*/
public function fileNotFoundErrorHandler($error_level, $message, $filename, $line) {
// Skip error handling if this is a "file not found" error.
if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
$this->getFilenameTestTriggeredError = $message;
return;
}
_drupal_error_handler($error_level, $message, $filename, $line);
}
/**
* Test that watchdog messages about missing files are correctly recorded.
*/
public function testWatchdog() {
// Search for a module that does not exist in either the file system or the
// {system} table. Make sure that an appropriate warning is recorded in the
// logs.
$non_existing_module = $this->randomName();
$query_parameters = array(
':type' => 'php',
':severity' => WATCHDOG_WARNING,
);
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND severity = :severity', $query_parameters)->fetchField(), 0, 'No warning message appears in the logs before searching for a module that does not exist.');
// Trigger the drupal_get_filename() call. This must be done via a request
// to a separate URL since the watchdog() will happen in a shutdown
// function, and so that SimpleTest can be told to ignore (and not fail as
// a result of) the expected PHP warnings generated during this process.
variable_set('system_test_drupal_get_filename_test_module_name', $non_existing_module);
$this->drupalGet('system-test/drupal-get-filename');
$message_variables = db_query('SELECT variables FROM {watchdog} WHERE type = :type AND severity = :severity', $query_parameters)->fetchCol();
$this->assertEqual(count($message_variables), 1, 'A single warning message appears in the logs after searching for a module that does not exist.');
$variables = reset($message_variables);
$variables = unserialize($variables);
$this->assertTrue(isset($variables['!message']) && strpos($variables['!message'], format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) !== FALSE, 'The warning message that appears in the logs after searching for a module that does not exist contains the expected text.');
}
/**
* Test that drupal_get_filename() does not break recursive rebuilds.
*/
public function testRecursiveRebuilds() {
// Ensure that the drupal_get_filename() call due to a missing module does
// not break the data returned by an attempted recursive rebuild. The code
// path which is tested is as follows:
// - Call drupal_get_schema().
// - Within a hook_schema() implementation, trigger a drupal_get_filename()
// search for a nonexistent module.
// - In the watchdog() call that results from that, trigger
// drupal_get_schema() again.
// Without some kind of recursion protection, this could cause the second
// drupal_get_schema() call to return incomplete results. This test ensures
// that does not happen.
$non_existing_module = $this->randomName();
variable_set('system_test_drupal_get_filename_test_module_name', $non_existing_module);
$this->drupalGet('system-test/drupal-get-filename-with-schema-rebuild');
$original_drupal_get_schema_tables = variable_get('system_test_drupal_get_filename_with_schema_rebuild_original_tables');
$final_drupal_get_schema_tables = variable_get('system_test_drupal_get_filename_with_schema_rebuild_final_tables');
$this->assertTrue(!empty($original_drupal_get_schema_tables));
$this->assertTrue(!empty($final_drupal_get_schema_tables));
$this->assertEqual($original_drupal_get_schema_tables, $final_drupal_get_schema_tables);
}
}
class BootstrapTimerTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Timer test',
'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.',
'group' => 'Bootstrap',
);
}
/**
* Test timer_read() to ensure it properly accumulates time when the timer
* started and stopped multiple times.
* @return
*/
function testTimer() {
timer_start('test');
sleep(1);
$this->assertTrue(timer_read('test') >= 1000, 'Timer measured 1 second of sleeping while running.');
sleep(1);
timer_stop('test');
$this->assertTrue(timer_read('test') >= 2000, 'Timer measured 2 seconds of sleeping after being stopped.');
timer_start('test');
sleep(1);
$this->assertTrue(timer_read('test') >= 3000, 'Timer measured 3 seconds of sleeping after being restarted.');
sleep(1);
$timer = timer_stop('test');
$this->assertTrue(timer_read('test') >= 4000, 'Timer measured 4 seconds of sleeping after being stopped for a second time.');
$this->assertEqual($timer['count'], 2, 'Timer counted 2 instances of being started.');
}
}
/**
* Test that resetting static variables works.
*/
class BootstrapResettableStaticTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Resettable static variables test',
'description' => 'Test that drupal_static() and drupal_static_reset() work.',
'group' => 'Bootstrap',
);
}
/**
* Test that a variable reference returned by drupal_static() gets reset when
* drupal_static_reset() is called.
*/
function testDrupalStatic() {
$name = __CLASS__ . '_' . __METHOD__;
$var = &drupal_static($name, 'foo');
$this->assertEqual($var, 'foo', 'Variable returned by drupal_static() was set to its default.');
// Call the specific reset and the global reset each twice to ensure that
// multiple resets can be issued without odd side effects.
$var = 'bar';
drupal_static_reset($name);
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of name-specific reset.');
$var = 'bar';
drupal_static_reset($name);
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of name-specific reset.');
$var = 'bar';
drupal_static_reset();
$this->assertEqual($var, 'foo', 'Variable was reset after first invocation of global reset.');
$var = 'bar';
drupal_static_reset();
$this->assertEqual($var, 'foo', 'Variable was reset after second invocation of global reset.');
}
}
/**
* Test miscellaneous functions in bootstrap.inc.
*/
class BootstrapMiscTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Miscellaneous bootstrap unit tests',
'description' => 'Test miscellaneous functions in bootstrap.inc.',
'group' => 'Bootstrap',
);
}
/**
* Test miscellaneous functions in bootstrap.inc.
*/
function testMisc() {
// Test drupal_array_merge_deep().
$link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => 'X', 'class' => array('a', 'b')), 'language' => 'en');
$link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('c', 'd')), 'html' => TRUE);
$expected = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')), 'language' => 'en', 'html' => TRUE);
$this->assertIdentical(drupal_array_merge_deep($link_options_1, $link_options_2), $expected, 'drupal_array_merge_deep() returned a properly merged array.');
}
/**
* Tests that the drupal_check_memory_limit() function works as expected.
*/
function testCheckMemoryLimit() {
// Test that a very reasonable amount of memory is available.
$this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
// Test an unlimited memory limit.
// The function should always return true if the memory limit is set to -1.
$this->assertTrue(drupal_check_memory_limit('9999999999YB', -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
// Test that even though we have 30MB of memory available - the function
// returns FALSE when given an upper limit for how much memory can be used.
$this->assertFalse(drupal_check_memory_limit('30MB', '16MB'), 'drupal_check_memory_limit() returns FALSE with a 16MB upper limit on a 30MB requirement.');
// Test that an equal amount of memory to the amount requested returns TRUE.
$this->assertTrue(drupal_check_memory_limit('30MB', '30MB'), 'drupal_check_memory_limit() returns TRUE when requesting 30MB on a 30MB requirement.');
}
}
/**
* Tests for overriding server variables via the API.
*/
class BootstrapOverrideServerVariablesTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
'name' => 'Overriding server variables',
'description' => 'Test that drupal_override_server_variables() works correctly.',
'group' => 'Bootstrap',
);
}
/**
* Test providing a direct URL to to drupal_override_server_variables().
*/
function testDrupalOverrideServerVariablesProvidedURL() {
$tests = array(
'http://example.com' => array(
'HTTP_HOST' => 'example.com',
'SCRIPT_NAME' => isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL,
),
'http://example.com/index.php' => array(
'HTTP_HOST' => 'example.com',
'SCRIPT_NAME' => '/index.php',
),
'http://example.com/subdirectory/index.php' => array(
'HTTP_HOST' => 'example.com',
'SCRIPT_NAME' => '/subdirectory/index.php',
),
);
foreach ($tests as $url => $expected_server_values) {
// Remember the original value of $_SERVER, since the function call below
// will modify it.
$original_server = $_SERVER;
// Call drupal_override_server_variables() and ensure that all expected
// $_SERVER variables were modified correctly.
drupal_override_server_variables(array('url' => $url));
foreach ($expected_server_values as $key => $value) {
$this->assertIdentical($_SERVER[$key], $value);
}
// Restore the original value of $_SERVER.
$_SERVER = $original_server;
}
}
}
/**
* Tests for $_GET['destination'] and $_REQUEST['destination'] validation.
*/
class BootstrapDestinationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'URL destination validation',
'description' => 'Test that $_GET[\'destination\'] and $_REQUEST[\'destination\'] cannot contain external URLs.',
'group' => 'Bootstrap',
);
}
function setUp() {
parent::setUp('system_test');
}
/**
* Tests that $_GET/$_REQUEST['destination'] only contain internal URLs.
*
* @see _drupal_bootstrap_variables()
* @see system_test_get_destination()
* @see system_test_request_destination()
*/
public function testDestination() {
$test_cases = array(
array(
'input' => 'node',
'output' => 'node',
'message' => "Standard internal example node path is present in the 'destination' parameter.",
),
array(
'input' => '/example.com',
'output' => '/example.com',
'message' => 'Internal path with one leading slash is allowed.',
),
array(
'input' => '//example.com/test',
'output' => '',
'message' => 'External URL without scheme is not allowed.',
),
array(
'input' => 'example:test',
'output' => 'example:test',
'message' => 'Internal URL using a colon is allowed.',
),
array(
'input' => 'http://example.com',
'output' => '',
'message' => 'External URL is not allowed.',
),
array(
'input' => 'javascript:alert(0)',
'output' => 'javascript:alert(0)',
'message' => 'Javascript URL is allowed because it is treated as an internal URL.',
),
);
foreach ($test_cases as $test_case) {
// Test $_GET['destination'].
$this->drupalGet('system-test/get-destination', array('query' => array('destination' => $test_case['input'])));
$this->assertIdentical($test_case['output'], $this->drupalGetContent(), $test_case['message']);
// Test $_REQUEST['destination']. There's no form to submit to, so
// drupalPost() won't work here; this just tests a direct $_POST request
// instead.
$curl_parameters = array(
CURLOPT_URL => $this->getAbsoluteUrl('system-test/request-destination'),
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => 'destination=' . urlencode($test_case['input']),
CURLOPT_HTTPHEADER => array(),
);
$post_output = $this->curlExec($curl_parameters);
$this->assertIdentical($test_case['output'], $post_output, $test_case['message']);
}
// Make sure that 404 pages do not populate $_GET['destination'] with
// external URLs.
variable_set('site_404', 'system-test/get-destination');
$this->drupalGet('http://example.com', array('external' => FALSE));
$this->assertIdentical('', $this->drupalGetContent(), 'External URL is not allowed on 404 pages.');
}
}
/**
* Tests DrupalCacheArray functionality.
*/
class BootstrapDrupalCacheArrayTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'DrupalCacheArray tests',
'description' => 'Test DrupalCacheArray functionality.',
'group' => 'Bootstrap',
);
}
/**
* Simulate unsafe deserialization of payload prepared by the phpggc project.
*
* @see https://github.com/ambionics/phpggc/pull/28
*/
public function testGadgetChainDrupal7RCE1() {
// phpggc -s Drupal7/RCE1 phpinfo 2
$payload = 'O:11:"SchemaCache":4:{s:6:"%00*%00cid"%3Bs:14:"form_DrupalRCE"%3Bs:6:"%00*%00bin"%3Bs:10:"cache_form"%3Bs:16:"%00*%00keysToPersist"%3Ba:3:{s:8:"#form_id"%3Bb:1%3Bs:8:"#process"%3Bb:1%3Bs:9:"#attached"%3Bb:1%3B}s:10:"%00*%00storage"%3Ba:3:{s:8:"#form_id"%3Bs:9:"DrupalRCE"%3Bs:8:"#process"%3Ba:1:{i:0%3Bs:23:"drupal_process_attached"%3B}s:9:"#attached"%3Ba:1:{s:7:"phpinfo"%3Ba:1:{i:0%3Ba:1:{i:0%3Bs:1:"2"%3B}}}}}';
$object = unserialize(urldecode($payload));
// The object then needs to be destructed.
unset($object);
// If the exploit was successful, there should now be a row in cache_form.
$payload2 = db_query_range('SELECT data FROM {cache_form} WHERE cid LIKE :cid', 0, 1, array(':cid' => 'form_DrupalRCE'))->fetchField();
$this->assertFalse(is_string($payload2) && (strpos($payload2, 'phpinfo') !== FALSE), 'Second stage payload was not written to cache_form.');
// The final exploit is executed via the ajax system, but is not a
// sufficiently valid ajax form submission to use drupalPost for testing.
$headers = array(
'Content-Type: application/x-www-form-urlencoded',
);
$curl_options = array(
CURLOPT_URL => url('system/ajax', array('absolute' => TRUE)),
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => 'form_build_id=DrupalRCE',
CURLOPT_HTTPHEADER => $headers,
);
// The second stage payload causes several PHP warnings / notices if it is
// there in cache_form.
$content = $this->curlExec($curl_options);
$this->assertFalse((strpos($content, 'Rasmus Lerdorf') !== FALSE), 'Remote Code Execution was not successful.');
// Now opt-out of the cache_form protection.
variable_set('drupal_cache_array_persist_cache_form', TRUE);
$object = unserialize(urldecode($payload));
// The object then needs to be destructed.
unset($object);
// If the exploit was successful, there should now be a row in cache_form.
$payload2 = db_query_range('SELECT data FROM {cache_form} WHERE cid LIKE :cid', 0, 1, array(':cid' => 'form_DrupalRCE'))->fetchField();
$this->assertTrue(is_string($payload2) && (strpos($payload2, 'phpinfo') !== FALSE), 'DrupalCacheArray persisted data to cache_form.');
}
}

View file

@ -0,0 +1,437 @@
<?php
class CacheTestCase extends DrupalWebTestCase {
protected $default_bin = 'cache';
protected $default_cid = 'test_temporary';
protected $default_value = 'CacheTest';
/**
* Check whether or not a cache entry exists.
*
* @param $cid
* The cache id.
* @param $var
* The variable the cache should contain.
* @param $bin
* The bin the cache item was stored in.
* @return
* TRUE on pass, FALSE on fail.
*/
protected function checkCacheExists($cid, $var, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
$cache = cache_get($cid, $bin);
return isset($cache->data) && $cache->data == $var;
}
/**
* Assert or a cache entry exists.
*
* @param $message
* Message to display.
* @param $var
* The variable the cache should contain.
* @param $cid
* The cache id.
* @param $bin
* The bin the cache item was stored in.
*/
protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
if ($cid == NULL) {
$cid = $this->default_cid;
}
if ($var == NULL) {
$var = $this->default_value;
}
$this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
}
/**
* Assert or a cache entry has been removed.
*
* @param $message
* Message to display.
* @param $cid
* The cache id.
* @param $bin
* The bin the cache item was stored in.
*/
function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
if ($cid == NULL) {
$cid = $this->default_cid;
}
$cache = cache_get($cid, $bin);
$this->assertFalse($cache, $message);
}
/**
* Perform the general wipe.
* @param $bin
* The bin to perform the wipe on.
*/
protected function generalWipe($bin = NULL) {
if ($bin == NULL) {
$bin = $this->default_bin;
}
cache_clear_all(NULL, $bin);
}
/**
* Setup the lifetime settings for caching.
*
* @param $time
* The time in seconds the cache should minimal live.
*/
protected function setupLifetime($time) {
variable_set('cache_lifetime', $time);
variable_set('cache_flush', 0);
}
}
class CacheSavingCase extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Cache saving test',
'description' => 'Check our variables are saved and restored the right way.',
'group' => 'Cache'
);
}
/**
* Test the saving and restoring of a string.
*/
function testString() {
$this->checkVariable($this->randomName(100));
}
/**
* Test the saving and restoring of an integer.
*/
function testInteger() {
$this->checkVariable(100);
}
/**
* Test the saving and restoring of a double.
*/
function testDouble() {
$this->checkVariable(1.29);
}
/**
* Test the saving and restoring of an array.
*/
function testArray() {
$this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
}
/**
* Test the saving and restoring of an object.
*/
function testObject() {
$test_object = new stdClass();
$test_object->test1 = $this->randomName(100);
$test_object->test2 = 100;
$test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
cache_set('test_object', $test_object, 'cache');
$cache = cache_get('test_object', 'cache');
$this->assertTrue(isset($cache->data) && $cache->data == $test_object, 'Object is saved and restored properly.');
}
/**
* Check or a variable is stored and restored properly.
*/
function checkVariable($var) {
cache_set('test_var', $var, 'cache');
$cache = cache_get('test_var', 'cache');
$this->assertTrue(isset($cache->data) && $cache->data === $var, format_string('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
}
/**
* Test no empty cids are written in cache table.
*/
function testNoEmptyCids() {
$this->drupalGet('user/register');
$this->assertFalse(cache_get(''), 'No cache entry is written with an empty cid.');
}
}
/**
* Test cache_get_multiple().
*/
class CacheGetMultipleUnitTest extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Fetching multiple cache items',
'description' => 'Confirm that multiple records are fetched correctly.',
'group' => 'Cache',
);
}
function setUp() {
$this->default_bin = 'cache_page';
parent::setUp();
}
/**
* Test cache_get_multiple().
*/
function testCacheMultiple() {
$item1 = $this->randomName(10);
$item2 = $this->randomName(10);
cache_set('item1', $item1, $this->default_bin);
cache_set('item2', $item2, $this->default_bin);
$this->assertTrue($this->checkCacheExists('item1', $item1), 'Item 1 is cached.');
$this->assertTrue($this->checkCacheExists('item2', $item2), 'Item 2 is cached.');
// Fetch both records from the database with cache_get_multiple().
$item_ids = array('item1', 'item2');
$items = cache_get_multiple($item_ids, $this->default_bin);
$this->assertEqual($items['item1']->data, $item1, 'Item was returned from cache successfully.');
$this->assertEqual($items['item2']->data, $item2, 'Item was returned from cache successfully.');
// Remove one item from the cache.
cache_clear_all('item2', $this->default_bin);
// Confirm that only one item is returned by cache_get_multiple().
$item_ids = array('item1', 'item2');
$items = cache_get_multiple($item_ids, $this->default_bin);
$this->assertEqual($items['item1']->data, $item1, 'Item was returned from cache successfully.');
$this->assertFalse(isset($items['item2']), 'Item was not returned from the cache.');
$this->assertTrue(count($items) == 1, 'Only valid cache entries returned.');
}
}
/**
* Test cache clearing methods.
*/
class CacheClearCase extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Cache clear test',
'description' => 'Check our clearing is done the proper way.',
'group' => 'Cache'
);
}
function setUp() {
$this->default_bin = 'cache_page';
$this->default_value = $this->randomName(10);
parent::setUp();
}
/**
* Test clearing using a cid.
*/
function testClearCid() {
cache_set('test_cid_clear', $this->default_value, $this->default_bin);
$this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
cache_clear_all('test_cid_clear', $this->default_bin);
$this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches were created for checking cid "*" with wildcard false.');
cache_clear_all('*', $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches still exists after clearing cid "*" with wildcard false.');
}
/**
* Test clearing using wildcard.
*/
function testClearWildcard() {
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches were created for checking cid "*" with wildcard true.');
cache_clear_all('*', $this->default_bin, TRUE);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches removed after clearing cid "*" with wildcard true.');
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches were created for checking cid substring with wildcard true.');
cache_clear_all('test_', $this->default_bin, TRUE);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two caches removed after clearing cid substring with wildcard true.');
}
/**
* Test clearing using an array.
*/
function testClearArray() {
// Create three cache entries.
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value)
&& $this->checkCacheExists('test_cid_clear3', $this->default_value),
'Three cache entries were created.');
// Clear two entries using an array.
cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two cache entries removed after clearing with an array.');
$this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
'Entry was not cleared from the cache');
// Set the cache clear threshold to 2 to confirm that the full bin is cleared
// when the threshold is exceeded.
variable_set('cache_clear_threshold', 2);
cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
$this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
&& $this->checkCacheExists('test_cid_clear2', $this->default_value),
'Two cache entries were created.');
cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
$this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
|| $this->checkCacheExists('test_cid_clear2', $this->default_value)
|| $this->checkCacheExists('test_cid_clear3', $this->default_value),
'All cache entries removed when the array exceeded the cache clear threshold.');
}
/**
* Test drupal_flush_all_caches().
*/
function testFlushAllCaches() {
// Create cache entries for each flushed cache bin.
$bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
$bins = array_merge(module_invoke_all('flush_caches'), $bins);
foreach ($bins as $id => $bin) {
$id = 'test_cid_clear' . $id;
cache_set($id, $this->default_value, $bin);
}
// Remove all caches then make sure that they are cleared.
drupal_flush_all_caches();
foreach ($bins as $id => $bin) {
$id = 'test_cid_clear' . $id;
$this->assertFalse($this->checkCacheExists($id, $this->default_value, $bin), format_string('All cache entries removed from @bin.', array('@bin' => $bin)));
}
}
/**
* Test DrupalDatabaseCache::isValidBin().
*/
function testIsValidBin() {
// Retrieve existing cache bins.
$valid_bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
$valid_bins = array_merge(module_invoke_all('flush_caches'), $valid_bins);
foreach ($valid_bins as $id => $bin) {
$cache = _cache_get_object($bin);
if ($cache instanceof DrupalDatabaseCache) {
$this->assertTrue($cache->isValidBin(), format_string('Cache bin @bin is valid.', array('@bin' => $bin)));
}
}
// Check for non-cache tables and invalid bins.
$invalid_bins = array('block', 'filter', 'missing_table', $this->randomName());
foreach ($invalid_bins as $id => $bin) {
$cache = _cache_get_object($bin);
if ($cache instanceof DrupalDatabaseCache) {
$this->assertFalse($cache->isValidBin(), format_string('Cache bin @bin is not valid.', array('@bin' => $bin)));
}
}
}
/**
* Test minimum cache lifetime.
*/
function testMinimumCacheLifetime() {
// Set a minimum/maximum cache lifetime.
$this->setupLifetime(300);
// Login as a newly-created user.
$account = $this->drupalCreateUser(array());
$this->drupalLogin($account);
// Set two cache objects in different bins.
$data = $this->randomName(100);
cache_set($data, $data, 'cache', CACHE_TEMPORARY);
$cached = cache_get($data);
$this->assertTrue(isset($cached->data) && $cached->data === $data, 'Cached item retrieved.');
cache_set($data, $data, 'cache_page', CACHE_TEMPORARY);
// Expire temporary items in the 'page' bin.
cache_clear_all(NULL, 'cache_page');
// Since the database cache uses REQUEST_TIME, set the $_SESSION variable
// manually to force it to the current time.
$_SESSION['cache_expiration']['cache_page'] = time();
// Items in the default cache bin should not be expired.
$cached = cache_get($data);
$this->assertTrue(isset($cached->data) && $cached->data == $data, 'Cached item retrieved');
// Despite the minimum cache lifetime, the item in the 'page' bin should
// be invalidated for the current user.
$cached = cache_get($data, 'cache_page');
$this->assertFalse($cached, 'Cached item was invalidated');
}
}
/**
* Test cache_is_empty() function.
*/
class CacheIsEmptyCase extends CacheTestCase {
public static function getInfo() {
return array(
'name' => 'Cache emptiness test',
'description' => 'Check if a cache bin is empty after performing clear operations.',
'group' => 'Cache'
);
}
function setUp() {
$this->default_bin = 'cache_page';
$this->default_value = $this->randomName(10);
parent::setUp();
}
/**
* Test clearing using a cid.
*/
function testIsEmpty() {
// Clear the cache bin.
cache_clear_all('*', $this->default_bin);
$this->assertTrue(cache_is_empty($this->default_bin), 'The cache bin is empty');
// Add some data to the cache bin.
cache_set($this->default_cid, $this->default_value, $this->default_bin);
$this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid);
$this->assertFalse(cache_is_empty($this->default_bin), 'The cache bin is not empty');
// Remove the cached data.
cache_clear_all($this->default_cid, $this->default_bin);
$this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid);
$this->assertTrue(cache_is_empty($this->default_bin), 'The cache bin is empty');
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
/* This file is for testing CSS file inclusion, no contents are necessary. */

View file

@ -0,0 +1,13 @@
name = "Common Test"
description = "Support module for Common tests."
package = Testing
version = VERSION
core = 7.x
stylesheets[all][] = common_test.css
stylesheets[print][] = common_test.print.css
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,324 @@
<?php
/**
* @file
* Helper module for the Common tests.
*/
/**
* Implements hook_menu().
*/
function common_test_menu() {
$items['common-test/drupal_goto'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_land',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/fail'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_land_fail',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/redirect'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_redirect',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/redirect_advanced'] = array(
'title' => 'Drupal Goto',
'page callback' => 'common_test_drupal_goto_redirect_advanced',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/drupal_goto/redirect_fail'] = array(
'title' => 'Drupal Goto Failure',
'page callback' => 'drupal_goto',
'page arguments' => array('common-test/drupal_goto/fail'),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/destination'] = array(
'title' => 'Drupal Get Destination',
'page callback' => 'common_test_destination',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/query-string'] = array(
'title' => 'Test querystring',
'page callback' => 'common_test_js_and_css_querystring',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
$items['common-test/html_head_link'] = array(
'title' => 'Test HTML head link',
'page callback' => 'common_test_html_head_link',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Redirect using drupal_goto().
*/
function common_test_drupal_goto_redirect() {
drupal_goto('common-test/drupal_goto');
}
/**
* Redirect using drupal_goto().
*/
function common_test_drupal_goto_redirect_advanced() {
drupal_goto('common-test/drupal_goto', array('query' => array('foo' => '123')), 301);
}
/**
* Landing page for drupal_goto().
*/
function common_test_drupal_goto_land() {
print "drupal_goto";
}
/**
* Fail landing page for drupal_goto().
*/
function common_test_drupal_goto_land_fail() {
print "drupal_goto_fail";
}
/**
* Implements hook_drupal_goto_alter().
*/
function common_test_drupal_goto_alter(&$path, &$options, &$http_response_code) {
if ($path == 'common-test/drupal_goto/fail') {
$path = 'common-test/drupal_goto/redirect';
}
}
/**
* Implements hook_init().
*/
function common_test_init() {
if (variable_get('common_test_redirect_current_path', FALSE)) {
drupal_goto(current_path());
}
if (variable_get('common_test_link_to_current_path', FALSE)) {
drupal_set_message(l('link which should point to the current path', current_path()));
}
}
/**
* Print destination query parameter.
*/
function common_test_destination() {
$destination = drupal_get_destination();
print "The destination: " . check_plain($destination['destination']);
}
/**
* Applies #printed to an element to help test #pre_render.
*/
function common_test_drupal_render_printing_pre_render($elements) {
$elements['#printed'] = TRUE;
return $elements;
}
/**
* Implements hook_TYPE_alter().
*/
function common_test_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
// Alter first argument.
if (is_array($data)) {
$data['foo'] = 'Drupal';
}
elseif (is_object($data)) {
$data->foo = 'Drupal';
}
// Alter second argument, if present.
if (isset($arg2)) {
if (is_array($arg2)) {
$arg2['foo'] = 'Drupal';
}
elseif (is_object($arg2)) {
$arg2->foo = 'Drupal';
}
}
// Try to alter third argument, if present.
if (isset($arg3)) {
if (is_array($arg3)) {
$arg3['foo'] = 'Drupal';
}
elseif (is_object($arg3)) {
$arg3->foo = 'Drupal';
}
}
}
/**
* Implements hook_TYPE_alter() on behalf of Bartik theme.
*
* Same as common_test_drupal_alter_alter(), but here, we verify that themes
* can also alter and come last.
*/
function bartik_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
// Alter first argument.
if (is_array($data)) {
$data['foo'] .= ' theme';
}
elseif (is_object($data)) {
$data->foo .= ' theme';
}
// Alter second argument, if present.
if (isset($arg2)) {
if (is_array($arg2)) {
$arg2['foo'] .= ' theme';
}
elseif (is_object($arg2)) {
$arg2->foo .= ' theme';
}
}
// Try to alter third argument, if present.
if (isset($arg3)) {
if (is_array($arg3)) {
$arg3['foo'] .= ' theme';
}
elseif (is_object($arg3)) {
$arg3->foo .= ' theme';
}
}
}
/**
* Implements hook_TYPE_alter() on behalf of block module.
*
* This is for verifying that drupal_alter(array(TYPE1, TYPE2), ...) allows
* hook_module_implements_alter() to affect the order in which module
* implementations are executed.
*/
function block_drupal_alter_foo_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
$data['foo'] .= ' block';
}
/**
* Implements hook_module_implements_alter().
*
* @see block_drupal_alter_foo_alter()
*/
function common_test_module_implements_alter(&$implementations, $hook) {
// For drupal_alter(array('drupal_alter', 'drupal_alter_foo'), ...), make the
// block module implementations run after all the other modules. Note that
// when drupal_alter() is called with an array of types, the first type is
// considered primary and controls the module order.
if ($hook == 'drupal_alter_alter' && isset($implementations['block'])) {
$group = $implementations['block'];
unset($implementations['block']);
$implementations['block'] = $group;
}
}
/**
* Implements hook_theme().
*/
function common_test_theme() {
return array(
'common_test_foo' => array(
'variables' => array('foo' => 'foo', 'bar' => 'bar'),
),
);
}
/**
* Theme function for testing drupal_render() theming.
*/
function theme_common_test_foo($variables) {
return $variables['foo'] . $variables['bar'];
}
/**
* Implements hook_library_alter().
*/
function common_test_library_alter(&$libraries, $module) {
if ($module == 'system' && isset($libraries['farbtastic'])) {
// Change the title of Farbtastic to "Farbtastic: Altered Library".
$libraries['farbtastic']['title'] = 'Farbtastic: Altered Library';
// Make Farbtastic depend on jQuery Form to test library dependencies.
$libraries['farbtastic']['dependencies'][] = array('system', 'form');
}
}
/**
* Implements hook_library().
*
* Adds Farbtastic in a different version.
*/
function common_test_library() {
$libraries['farbtastic'] = array(
'title' => 'Custom Farbtastic Library',
'website' => 'http://code.google.com/p/farbtastic/',
'version' => '5.3',
'js' => array(
'misc/farbtastic/farbtastic.js' => array(),
),
'css' => array(
'misc/farbtastic/farbtastic.css' => array(),
),
);
return $libraries;
}
/**
* Adds a JavaScript file and a CSS file with a query string appended.
*/
function common_test_js_and_css_querystring() {
drupal_add_js(drupal_get_path('module', 'node') . '/node.js');
drupal_add_css(drupal_get_path('module', 'node') . '/node.css');
// A relative URI may have a query string.
drupal_add_css('/' . drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2');
return '';
}
/**
* Implements hook_cron().
*
* System module should handle if a module does not catch an exception and keep
* cron going.
*
* @see common_test_cron_helper()
*
*/
function common_test_cron() {
throw new Exception(t('Uncaught exception'));
}
/**
* Page callback.
*/
function common_test_html_head_link() {
drupal_add_html_head_link(array(
'href' => '/foo?bar=baz',
'rel' => 'alternate',
), TRUE);
drupal_add_html_head_link(array(
'href' => '/not-added-to-http-headers',
'rel' => 'alternate',
), FALSE);
drupal_add_html_head_link(array(
'href' => '/foo/bar',
'hreflang' => 'nl',
'rel' => 'alternate',
), TRUE);
drupal_add_html_head_link(array(
'href' => '/foo/bar',
'hreflang' => 'de',
'rel' => 'alternate',
), TRUE);
drupal_add_html_head_link(array(
'href' => '/foo?bar=baz&baz=false',
'hreflang' => 'en',
'rel' => 'alternate',
), TRUE);
return '';
}

View file

@ -0,0 +1,2 @@
/* This file is for testing CSS file inclusion, no contents are necessary. */

View file

@ -0,0 +1,11 @@
name = "Common Test Cron Helper"
description = "Helper module for CronRunTestCase::testCronExceptions()."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,17 @@
<?php
/**
* @file
* Helper module for the testCronExceptions in addition to common_test module.
*/
/**
* Implements hook_cron().
*
* common_test_cron() throws an exception, but the execution should reach this
* function as well.
*
* @see common_test_cron()
*/
function common_test_cron_helper_cron() {
variable_set('common_test_cron', 'success');
}

View file

@ -0,0 +1,9 @@
; Test parsing with a simple string.
simple_string = A simple string
; Test that constants can be used as values.
simple_constant = WATCHDOG_INFO
; After parsing the .info file, 'double_colon' should hold the literal value.
; Parsing should not throw a fatal error or try to access a class constant.
double_colon = dummyClassName::

View file

@ -0,0 +1,11 @@
name = "Database Test"
description = "Support module for Database layer tests."
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,300 @@
<?php
/**
* @file
* Install, update and uninstall functions for the database_test module.
*/
/**
* Implements hook_schema().
*
* The database tests use the database API which depends on schema
* information for certain operations on certain databases.
* Therefore, the schema must actually be declared in a normal module
* like any other, not directly in the test file.
*/
function database_test_schema() {
$schema['test'] = array(
'description' => 'Basic test table for the database unit tests.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'name' => array(
'description' => "A person's name",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'binary' => TRUE,
),
'age' => array(
'description' => "The person's age",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'job' => array(
'description' => "The person's job",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => 'Undefined',
),
),
'primary key' => array('id'),
'unique keys' => array(
'name' => array('name')
),
'indexes' => array(
'ages' => array('age'),
),
);
$schema['test_classtype'] = array(
'description' => 'A duplicate version of the test table, used for fetch_style PDO::FETCH_CLASSTYPE tests.',
'fields' => array(
'classname' => array(
'description' => "A custom class name",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'name' => array(
'description' => "A person's name",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'age' => array(
'description' => "The person's age",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'job' => array(
'description' => "The person's job",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'primary key' => array('job'),
'indexes' => array(
'ages' => array('age'),
),
);
// This is an alternate version of the same table that is structured the same
// but has a non-serial Primary Key.
$schema['test_people'] = array(
'description' => 'A duplicate version of the test table, used for additional tests.',
'fields' => array(
'name' => array(
'description' => "A person's name",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'age' => array(
'description' => "The person's age",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'job' => array(
'description' => "The person's job",
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'primary key' => array('job'),
'indexes' => array(
'ages' => array('age'),
),
);
$schema['test_people_copy'] = $schema['test_people'];
$schema['test_people_copy']['description'] = 'A duplicate version of the test_people table, used for additional tests.';
$schema['test_one_blob'] = array(
'description' => 'A simple table including a BLOB field for testing BLOB behavior.',
'fields' => array(
'id' => array(
'description' => 'Simple unique ID.',
'type' => 'serial',
'not null' => TRUE,
),
'blob1' => array(
'description' => 'A BLOB field.',
'type' => 'blob',
),
),
'primary key' => array('id'),
);
$schema['test_two_blobs'] = array(
'description' => 'A simple test table with two BLOB fields.',
'fields' => array(
'id' => array(
'description' => 'Simple unique ID.',
'type' => 'serial',
'not null' => TRUE,
),
'blob1' => array(
'description' => 'A dummy BLOB field.',
'type' => 'blob',
),
'blob2' => array(
'description' => 'A second BLOB field.',
'type' => 'blob'
),
),
'primary key' => array('id'),
);
$schema['test_task'] = array(
'description' => 'A task list for people in the test table.',
'fields' => array(
'tid' => array(
'description' => 'Task ID, primary key.',
'type' => 'serial',
'not null' => TRUE,
),
'pid' => array(
'description' => 'The {test_people}.pid, foreign key for the test table.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'task' => array(
'description' => 'The task to be completed.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'priority' => array(
'description' => 'The priority of the task.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('tid'),
);
$schema['test_null'] = array(
'description' => 'Basic test table for NULL value handling.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'name' => array(
'description' => "A person's name.",
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => '',
),
'age' => array(
'description' => "The person's age.",
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'default' => 0),
),
'primary key' => array('id'),
'unique keys' => array(
'name' => array('name')
),
'indexes' => array(
'ages' => array('age'),
),
);
$schema['test_serialized'] = array(
'description' => 'Basic test table for NULL value handling.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'name' => array(
'description' => "A person's name.",
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => '',
),
'info' => array(
'description' => "The person's data in serialized form.",
'type' => 'blob',
'serialize' => TRUE,
),
),
'primary key' => array('id'),
'unique keys' => array(
'name' => array('name')
),
);
$schema['virtual'] = array(
'description' => 'Basic test table with a reserved name.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'function' => array(
'description' => "A column with a reserved name.",
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => '',
),
),
'primary key' => array('id'),
);
$schema['TEST_UPPERCASE'] = $schema['test'];
$schema['test_db_specific_datatype'] = array(
'description' => 'Schema with database specific data type.',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'age' => array(
'description' => "The person's age.",
'mysql_type' => 'tinyint',
'pgsql_type' => 'smallint',
'sqlite_type' => 'tinyint',
'not null' => FALSE,
'default' => 0,
),
),
'primary key' => array('id'),
);
return $schema;
}

View file

@ -0,0 +1,241 @@
<?php
/**
* Implements hook_query_alter().
*/
function database_test_query_alter(QueryAlterableInterface $query) {
if ($query->hasTag('database_test_alter_add_range')) {
$query->range(0, 2);
}
if ($query->hasTag('database_test_alter_add_join')) {
$people_alias = $query->join('test', 'people', "test_task.pid = %alias.id");
$name_field = $query->addField($people_alias, 'name', 'name');
$query->condition($people_alias . '.id', 2);
}
if ($query->hasTag('database_test_alter_change_conditional')) {
$conditions =& $query->conditions();
$conditions[0]['value'] = 2;
}
if ($query->hasTag('database_test_alter_change_fields')) {
$fields =& $query->getFields();
unset($fields['age']);
}
if ($query->hasTag('database_test_alter_change_expressions')) {
$expressions =& $query->getExpressions();
$expressions['double_age']['expression'] = 'age*3';
}
}
/**
* Implements hook_query_TAG_alter().
*
* Called by DatabaseTestCase::testAlterRemoveRange.
*/
function database_test_query_database_test_alter_remove_range_alter(QueryAlterableInterface $query) {
$query->range();
}
/**
* Implements hook_menu().
*/
function database_test_menu() {
$items['database_test/db_query_temporary'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_db_query_temporary',
);
$items['database_test/pager_query_even'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_even_pager_query',
);
$items['database_test/pager_query_odd'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_odd_pager_query',
);
$items['database_test/tablesort'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_tablesort',
);
$items['database_test/tablesort_first'] = array(
'access callback' => TRUE,
'page callback' => 'database_test_tablesort_first',
);
$items['database_test/tablesort_default_sort'] = array(
'access callback' => TRUE,
'page callback' => 'drupal_get_form',
'page arguments' => array('database_test_theme_tablesort'),
);
return $items;
}
/**
* Run a db_query_temporary and output the table name and its number of rows.
*
* We need to test that the table created is temporary, so we run it here, in a
* separate menu callback request; After this request is done, the temporary
* table should automatically dropped.
*/
function database_test_db_query_temporary() {
$table_name = db_query_temporary('SELECT status FROM {system}', array());
drupal_json_output(array(
'table_name' => $table_name,
'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(),
));
exit;
}
/**
* Run a pager query and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_even_pager_query($limit) {
$query = db_select('test', 't');
$query
->fields('t', array('name'))
->orderBy('age');
// This should result in 2 pages of results.
$query = $query->extend('PagerDefault')->limit($limit);
$names = $query->execute()->fetchCol();
drupal_json_output(array(
'names' => $names,
));
exit;
}
/**
* Run a pager query and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_odd_pager_query($limit) {
$query = db_select('test_task', 't');
$query
->fields('t', array('task'))
->orderBy('pid');
// This should result in 4 pages of results.
$query = $query->extend('PagerDefault')->limit($limit);
$names = $query->execute()->fetchCol();
drupal_json_output(array(
'names' => $names,
));
exit;
}
/**
* Run a tablesort query and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_tablesort() {
$header = array(
'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'),
'pid' => array('data' => t('Person ID'), 'field' => 'pid'),
'task' => array('data' => t('Task'), 'field' => 'task'),
'priority' => array('data' => t('Priority'), 'field' => 'priority', ),
);
$query = db_select('test_task', 't');
$query
->fields('t', array('tid', 'pid', 'task', 'priority'));
$query = $query->extend('TableSort')->orderByHeader($header);
// We need all the results at once to check the sort.
$tasks = $query->execute()->fetchAll();
drupal_json_output(array(
'tasks' => $tasks,
));
exit;
}
/**
* Run a tablesort query with a second order_by after and return the results.
*
* This function does care about the page GET parameter, as set by the
* simpletest HTTP call.
*/
function database_test_tablesort_first() {
$header = array(
'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'),
'pid' => array('data' => t('Person ID'), 'field' => 'pid'),
'task' => array('data' => t('Task'), 'field' => 'task'),
'priority' => array('data' => t('Priority'), 'field' => 'priority', ),
);
$query = db_select('test_task', 't');
$query
->fields('t', array('tid', 'pid', 'task', 'priority'));
$query = $query->extend('TableSort')->orderByHeader($header)->orderBy('priority');
// We need all the results at once to check the sort.
$tasks = $query->execute()->fetchAll();
drupal_json_output(array(
'tasks' => $tasks,
));
exit;
}
/**
* Output a form without setting a header sort.
*/
function database_test_theme_tablesort($form, &$form_state) {
$header = array(
'username' => array('data' => t('Username'), 'field' => 'u.name'),
'status' => array('data' => t('Status'), 'field' => 'u.status'),
);
$query = db_select('users', 'u');
$query->condition('u.uid', 0, '<>');
user_build_filter_query($query);
$count_query = clone $query;
$count_query->addExpression('COUNT(u.uid)');
$query = $query->extend('PagerDefault')->extend('TableSort');
$query
->fields('u', array('uid', 'name', 'status', 'created', 'access'))
->limit(50)
->orderByHeader($header)
->setCountQuery($count_query);
$result = $query->execute();
$options = array();
$status = array(t('blocked'), t('active'));
$accounts = array();
foreach ($result as $account) {
$options[$account->uid] = array(
'username' => check_plain($account->name),
'status' => $status[$account->status],
);
}
$form['accounts'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No people available.'),
);
return $form;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
name = "Drupal code registry test"
description = "Support module for testing the code registry."
files[] = drupal_autoload_test_interface.inc
files[] = drupal_autoload_test_class.inc
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,22 @@
<?php
/**
* @file
* Test module to check code registry.
*/
/**
* Implements hook_registry_files_alter().
*/
function drupal_autoload_test_registry_files_alter(&$files, $modules) {
foreach ($modules as $module) {
// Add the drupal_autoload_test_trait.sh file to the registry when PHP 5.4+
// is being used.
if ($module->name == 'drupal_autoload_test' && version_compare(PHP_VERSION, '5.4') >= 0) {
$files["$module->dir/drupal_autoload_test_trait.sh"] = array(
'module' => $module->name,
'weight' => $module->weight,
);
}
}
}

View file

@ -0,0 +1,11 @@
<?php
/**
* @file
* Test classes for code registry testing.
*/
/**
* This class is empty because we only care if Drupal can find it.
*/
class DrupalAutoloadTestClass implements DrupalAutoloadTestInterface {}

View file

@ -0,0 +1,11 @@
<?php
/**
* @file
* Test interfaces for code registry testing.
*/
/**
* This interface is empty because we only care if Drupal can find it.
*/
interface DrupalAutoloadTestInterface {}

View file

@ -0,0 +1,16 @@
<?php
/**
* @file
* Test traits for code registry testing.
*
* This file has a non-standard extension to prevent PHP < 5.4 testbots from
* trying to run a syntax check on it.
* @todo Use a standard extension once the testbots allow it. See
* https://www.drupal.org/node/2589649.
*/
/**
* This trait is empty because we only care if Drupal can find it.
*/
trait DrupalAutoloadTestTrait {}

View file

@ -0,0 +1,11 @@
name = "Drupal system listing compatible test"
description = "Support module for testing the drupal_system_listing function."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,11 @@
name = "Drupal system listing incompatible test"
description = "Support module for testing the drupal_system_listing function."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,12 @@
name = "Entity cache test"
description = "Support module for testing entity cache."
package = Testing
version = VERSION
core = 7.x
dependencies[] = entity_cache_test_dependency
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,27 @@
<?php
/**
* @file
* Helper module for entity cache tests.
*/
/**
* Implements hook_watchdog().
*
* This hook is called during module_enable() and since this hook
* implementation is invoked, we have to expect that this module and dependent
* modules have been properly installed already. So we expect to be able to
* retrieve the entity information that has been registered by the required
* dependency module.
*
* @see EnableDisableTestCase::testEntityCache()
* @see entity_cache_test_dependency_entity_info()
*/
function entity_cache_test_watchdog($log_entry) {
if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
$info = entity_get_info('entity_cache_test');
// Store the information in a system variable to analyze it later in the
// test case.
variable_set('entity_cache_test', $info);
}
}

View file

@ -0,0 +1,11 @@
name = "Entity cache test dependency"
description = "Support dependency module for testing entity cache."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,17 @@
<?php
/**
* @file
* Helper module for entity cache tests.
*/
/**
* Implements hook_entity_info().
*/
function entity_cache_test_dependency_entity_info() {
return array(
'entity_cache_test' => array(
'label' => variable_get('entity_cache_test_label', 'Entity Cache Test'),
),
);
}

View file

@ -0,0 +1,61 @@
<?php
/**
* @file
* Tests for the Entity CRUD API.
*/
/**
* Tests the entity_load() function.
*/
class EntityLoadTestCase extends DrupalWebTestCase {
protected $profile = 'testing';
public static function getInfo() {
return array(
'name' => 'Entity loading',
'description' => 'Tests the entity_load() function.',
'group' => 'Entity API',
);
}
/**
* Tests the functionality for loading entities matching certain conditions.
*/
public function testEntityLoadConditions() {
// Create a few nodes. One of them is given an edge-case title of "Array",
// because loading entities by an array of conditions is subject to PHP
// array-to-string conversion issues and we want to test those.
$node_1 = $this->drupalCreateNode(array('title' => 'Array'));
$node_2 = $this->drupalCreateNode(array('title' => 'Node 2'));
$node_3 = $this->drupalCreateNode(array('title' => 'Node 3'));
// Load all entities so that they are statically cached.
$all_nodes = entity_load('node', FALSE);
// Check that the first node can be loaded by title.
$nodes_loaded = entity_load('node', FALSE, array('title' => 'Array'));
$this->assertEqual(array_keys($nodes_loaded), array($node_1->nid));
// Check that the second and third nodes can be loaded by title using an
// array of conditions, and that the first node is not loaded from the
// cache along with them.
$nodes_loaded = entity_load('node', FALSE, array('title' => array('Node 2', 'Node 3')));
ksort($nodes_loaded);
$this->assertEqual(array_keys($nodes_loaded), array($node_2->nid, $node_3->nid));
$this->assertIdentical($nodes_loaded[$node_2->nid], $all_nodes[$node_2->nid], 'Loaded node 2 is identical to cached node.');
$this->assertIdentical($nodes_loaded[$node_3->nid], $all_nodes[$node_3->nid], 'Loaded node 3 is identical to cached node.');
}
public function testEntityLoadIds() {
$this->drupalCreateNode(array('title' => 'Node 1'));
$this->drupalCreateNode(array('title' => 'Node 2'));
$nodes_loaded = entity_load('node', array('1', '2'));
$this->assertEqual(count($nodes_loaded), 2);
// Ensure that an id with a trailing decimal place is ignored.
$nodes_loaded = entity_load('node', array('1.', '2'));
$this->assertEqual(count($nodes_loaded), 1);
}
}

View file

@ -0,0 +1,11 @@
name = "Entity CRUD Hooks Test"
description = "Support module for CRUD hook tests."
core = 7.x
package = Testing
version = VERSION
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,251 @@
<?php
/**
* @file
* Test module for the Entity CRUD API.
*/
/**
* Implements hook_entity_presave().
*/
function entity_crud_hook_test_entity_presave($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_presave().
*/
function entity_crud_hook_test_comment_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_presave().
*/
function entity_crud_hook_test_file_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_presave().
*/
function entity_crud_hook_test_node_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_presave().
*/
function entity_crud_hook_test_taxonomy_term_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_presave().
*/
function entity_crud_hook_test_taxonomy_vocabulary_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_presave().
*/
function entity_crud_hook_test_user_presave() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_insert().
*/
function entity_crud_hook_test_entity_insert($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_insert().
*/
function entity_crud_hook_test_comment_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_insert().
*/
function entity_crud_hook_test_file_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_insert().
*/
function entity_crud_hook_test_node_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_insert().
*/
function entity_crud_hook_test_taxonomy_term_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_insert().
*/
function entity_crud_hook_test_taxonomy_vocabulary_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_insert().
*/
function entity_crud_hook_test_user_insert() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_load().
*/
function entity_crud_hook_test_entity_load(array $entities, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_load().
*/
function entity_crud_hook_test_comment_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_load().
*/
function entity_crud_hook_test_file_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_load().
*/
function entity_crud_hook_test_node_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_load().
*/
function entity_crud_hook_test_taxonomy_term_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_load().
*/
function entity_crud_hook_test_taxonomy_vocabulary_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_load().
*/
function entity_crud_hook_test_user_load() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_update().
*/
function entity_crud_hook_test_entity_update($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_update().
*/
function entity_crud_hook_test_comment_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_update().
*/
function entity_crud_hook_test_file_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_update().
*/
function entity_crud_hook_test_node_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_update().
*/
function entity_crud_hook_test_taxonomy_term_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_update().
*/
function entity_crud_hook_test_taxonomy_vocabulary_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_update().
*/
function entity_crud_hook_test_user_update() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_entity_delete().
*/
function entity_crud_hook_test_entity_delete($entity, $type) {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
}
/**
* Implements hook_comment_delete().
*/
function entity_crud_hook_test_comment_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_file_delete().
*/
function entity_crud_hook_test_file_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_node_delete().
*/
function entity_crud_hook_test_node_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_term_delete().
*/
function entity_crud_hook_test_taxonomy_term_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_taxonomy_vocabulary_delete().
*/
function entity_crud_hook_test_taxonomy_vocabulary_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}
/**
* Implements hook_user_delete().
*/
function entity_crud_hook_test_user_delete() {
$_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
}

View file

@ -0,0 +1,338 @@
<?php
/**
* @file
* CRUD hook tests for the Entity CRUD API.
*/
/**
* Tests invocation of hooks when performing an action.
*
* Tested hooks are:
* - hook_entity_insert()
* - hook_entity_load()
* - hook_entity_update()
* - hook_entity_delete()
* As well as all type-specific hooks, like hook_node_insert(),
* hook_comment_update(), etc.
*/
class EntityCrudHookTestCase extends DrupalWebTestCase {
protected $ids = array();
public static function getInfo() {
return array(
'name' => 'Entity CRUD hooks',
'description' => 'Tests the invocation of hooks when inserting, loading, updating or deleting an entity.',
'group' => 'Entity API',
);
}
public function setUp() {
parent::setUp('entity_crud_hook_test', 'taxonomy', 'comment');
}
/**
* Pass if the message $text was set by one of the CRUD hooks in
* entity_crud_hook_test.module, i.e., if the $text is an element of
* $_SESSION['entity_crud_hook_test'].
*
* @param $text
* Plain text to look for.
* @param $message
* Message to display.
* @param $group
* The group this message belongs to, defaults to 'Other'.
* @return
* TRUE on pass, FALSE on fail.
*/
protected function assertHookMessage($text, $message = NULL, $group = 'Other') {
if (!isset($message)) {
$message = $text;
}
return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
}
/**
* Tests hook invocations for CRUD operations on comments.
*/
public function testCommentHooks() {
$node = (object) array(
'uid' => 1,
'type' => 'article',
'title' => 'Test node',
'status' => 1,
'comment' => 2,
'promote' => 0,
'sticky' => 0,
'language' => LANGUAGE_NONE,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
node_save($node);
$nid = $node->nid;
$comment = (object) array(
'cid' => NULL,
'pid' => 0,
'nid' => $nid,
'uid' => 1,
'subject' => 'Test comment',
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
'status' => 1,
'language' => LANGUAGE_NONE,
);
$_SESSION['entity_crud_hook_test'] = array();
comment_save($comment);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$comment = comment_load($comment->cid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_load called');
$_SESSION['entity_crud_hook_test'] = array();
$comment->subject = 'New subject';
comment_save($comment);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_update called');
$_SESSION['entity_crud_hook_test'] = array();
comment_delete($comment->cid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment');
$this->assertHookMessage('entity_crud_hook_test_comment_delete called');
}
/**
* Tests hook invocations for CRUD operations on files.
*/
public function testFileHooks() {
$url = 'public://entity_crud_hook_test.file';
file_put_contents($url, 'Test test test');
$file = (object) array(
'fid' => NULL,
'uid' => 1,
'filename' => 'entity_crud_hook_test.file',
'uri' => $url,
'filemime' => 'text/plain',
'filesize' => filesize($url),
'status' => 1,
'timestamp' => REQUEST_TIME,
);
$_SESSION['entity_crud_hook_test'] = array();
file_save($file);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$file = file_load($file->fid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_load called');
$_SESSION['entity_crud_hook_test'] = array();
$file->filename = 'new.entity_crud_hook_test.file';
file_save($file);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_update called');
$_SESSION['entity_crud_hook_test'] = array();
file_delete($file);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file');
$this->assertHookMessage('entity_crud_hook_test_file_delete called');
}
/**
* Tests hook invocations for CRUD operations on nodes.
*/
public function testNodeHooks() {
$node = (object) array(
'uid' => 1,
'type' => 'article',
'title' => 'Test node',
'status' => 1,
'comment' => 2,
'promote' => 0,
'sticky' => 0,
'language' => LANGUAGE_NONE,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
$_SESSION['entity_crud_hook_test'] = array();
node_save($node);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$node = node_load($node->nid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_load called');
$_SESSION['entity_crud_hook_test'] = array();
$node->title = 'New title';
node_save($node);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_update called');
$_SESSION['entity_crud_hook_test'] = array();
node_delete($node->nid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node');
$this->assertHookMessage('entity_crud_hook_test_node_delete called');
}
/**
* Tests hook invocations for CRUD operations on taxonomy terms.
*/
public function testTaxonomyTermHooks() {
$vocabulary = (object) array(
'name' => 'Test vocabulary',
'machine_name' => 'test',
'description' => NULL,
'module' => 'entity_crud_hook_test',
);
taxonomy_vocabulary_save($vocabulary);
$term = (object) array(
'vid' => $vocabulary->vid,
'name' => 'Test term',
'description' => NULL,
'format' => 1,
);
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_term_save($term);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$term = taxonomy_term_load($term->tid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_load called');
$_SESSION['entity_crud_hook_test'] = array();
$term->name = 'New name';
taxonomy_term_save($term);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called');
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_term_delete($term->tid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called');
}
/**
* Tests hook invocations for CRUD operations on taxonomy vocabularies.
*/
public function testTaxonomyVocabularyHooks() {
$vocabulary = (object) array(
'name' => 'Test vocabulary',
'machine_name' => 'test',
'description' => NULL,
'module' => 'entity_crud_hook_test',
);
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_vocabulary_save($vocabulary);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$vocabulary = taxonomy_vocabulary_load($vocabulary->vid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_load called');
$_SESSION['entity_crud_hook_test'] = array();
$vocabulary->name = 'New name';
taxonomy_vocabulary_save($vocabulary);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called');
$_SESSION['entity_crud_hook_test'] = array();
taxonomy_vocabulary_delete($vocabulary->vid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary');
$this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called');
}
/**
* Tests hook invocations for CRUD operations on users.
*/
public function testUserHooks() {
$edit = array(
'name' => 'Test user',
'mail' => 'test@example.com',
'created' => REQUEST_TIME,
'status' => 1,
'language' => 'en',
);
$account = (object) $edit;
$_SESSION['entity_crud_hook_test'] = array();
$account = user_save($account, $edit);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_insert called');
$_SESSION['entity_crud_hook_test'] = array();
$account = user_load($account->uid);
$this->assertHookMessage('entity_crud_hook_test_entity_load called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_load called');
$_SESSION['entity_crud_hook_test'] = array();
$edit['name'] = 'New name';
$account = user_save($account, $edit);
$this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_presave called');
$this->assertHookMessage('entity_crud_hook_test_entity_update called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_update called');
$_SESSION['entity_crud_hook_test'] = array();
user_delete($account->uid);
$this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user');
$this->assertHookMessage('entity_crud_hook_test_user_delete called');
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
name = "Entity query access test"
description = "Support module for checking entity query results."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Helper module for testing EntityFieldQuery access on any type of entity.
*/
/**
* Implements hook_menu().
*/
function entity_query_access_test_menu() {
$items['entity-query-access/test/%'] = array(
'title' => "Retrieve a sample of entity query access data",
'page callback' => 'entity_query_access_test_sample_query',
'page arguments' => array(2),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Returns the results from an example EntityFieldQuery.
*/
function entity_query_access_test_sample_query($field_name) {
global $user;
// Simulate user does not have access to view all nodes.
$access = &drupal_static('node_access_view_all_nodes');
$access[$user->uid] = FALSE;
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'test_entity_bundle_key')
->fieldCondition($field_name, 'value', 0, '>')
->entityOrderBy('entity_id', 'ASC');
$results = array(
'items' => array(),
'title' => t('EntityFieldQuery results'),
);
foreach ($query->execute() as $entity_type => $entity_ids) {
foreach ($entity_ids as $entity_id => $entity_stub) {
$results['items'][] = format_string('Found entity of type @entity_type with id @entity_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id));
}
}
if (count($results['items']) > 0) {
$output = theme('item_list', $results);
}
else {
$output = 'No results found with EntityFieldQuery.';
}
return $output;
}

View file

@ -0,0 +1,115 @@
<?php
/**
* Tests Drupal error and exception handlers.
*/
class DrupalErrorHandlerTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Drupal error handlers',
'description' => 'Performs tests on the Drupal error and exception handler.',
'group' => 'System',
);
}
function setUp() {
parent::setUp('error_test');
}
/**
* Test the error handler.
*/
function testErrorHandler() {
$error_notice = array(
'%type' => 'Notice',
'!message' => 'Object of class stdClass could not be converted to int',
'%function' => 'error_test_generate_warnings()',
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$error_warning = array(
'%type' => 'Warning',
'!message' => \PHP_VERSION_ID < 80000 ? 'Invalid argument supplied for foreach()' : 'foreach() argument must be of type array|object, string given',
'%function' => 'error_test_generate_warnings()',
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$error_user_notice = array(
'%type' => 'User warning',
'!message' => 'Drupal is awesome',
'%function' => 'error_test_generate_warnings()',
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
// Set error reporting to collect notices.
variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL);
$this->drupalGet('error-test/generate-warnings');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertErrorMessage($error_notice);
$this->assertErrorMessage($error_warning);
$this->assertErrorMessage($error_user_notice);
// Set error reporting to not collect notices.
variable_set('error_level', ERROR_REPORTING_DISPLAY_SOME);
$this->drupalGet('error-test/generate-warnings');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertNoErrorMessage($error_notice);
$this->assertErrorMessage($error_warning);
$this->assertErrorMessage($error_user_notice);
// Set error reporting to not show any errors.
variable_set('error_level', ERROR_REPORTING_HIDE);
$this->drupalGet('error-test/generate-warnings');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertNoErrorMessage($error_notice);
$this->assertNoErrorMessage($error_warning);
$this->assertNoErrorMessage($error_user_notice);
}
/**
* Test the exception handler.
*/
function testExceptionHandler() {
$error_exception = array(
'%type' => 'Exception',
'!message' => 'Drupal is awesome',
'%function' => 'error_test_trigger_exception()',
'%line' => 57,
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$error_pdo_exception = array(
'%type' => 'PDOException',
'!message' => 'SELECT * FROM bananas_are_awesome',
'%function' => 'error_test_trigger_pdo_exception()',
'%line' => 65,
'%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
);
$this->drupalGet('error-test/trigger-exception');
$this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
$this->assertErrorMessage($error_exception);
$this->drupalGet('error-test/trigger-pdo-exception');
$this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
// We cannot use assertErrorMessage() since the extact error reported
// varies from database to database. Check that the SQL string is displayed.
$this->assertText($error_pdo_exception['%type'], format_string('Found %type in error page.', $error_pdo_exception));
$this->assertText($error_pdo_exception['!message'], format_string('Found !message in error page.', $error_pdo_exception));
$error_details = format_string('in %function (line ', $error_pdo_exception);
$this->assertRaw($error_details, format_string("Found '!message' in error page.", array('!message' => $error_details)));
}
/**
* Helper function: assert that the error message is found.
*/
function assertErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error);
$this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message)));
}
/**
* Helper function: assert that the error message is not found.
*/
function assertNoErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error);
$this->assertNoRaw($message, format_string('Did not find error message: !message.', array('!message' => $message)));
}
}

View file

@ -0,0 +1,11 @@
name = "Error test"
description = "Support module for error and exception testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2024-03-06
version = "7.100"
project = "drupal"
datestamp = "1709734591"

Some files were not shown because too many files have changed in this diff Show more