diff --git a/classes/testing.class.php b/classes/testing.class.php
new file mode 100644
index 00000000..89beda3c
--- /dev/null
+++ b/classes/testing.class.php
@@ -0,0 +1,150 @@
+
+
+class Testing {
+ private static $ClassDirectories = array("classes");
+ private static $Classes = array();
+
+ /**
+ * Initialize the testasble classes into a map keyed by class name
+ */
+ public static function init() {
+ self::load_classes();
+ }
+
+ /**
+ * Gets the class
+ */
+ public static function get_classes() {
+ return self::$Classes;
+ }
+
+ /**
+ * Loads all the classes within given directories
+ */
+ private static function load_classes() {
+ foreach (self::$ClassDirectories as $Directory) {
+ $Directory = SERVER_ROOT . "/" . $Directory . "/";
+ foreach (glob($Directory . "*.php") as $FileName) {
+ self::get_class_name($FileName);
+ }
+ }
+ }
+
+ /**
+ * Gets the class and adds into the map
+ */
+ private static function get_class_name($FileName) {
+ $Tokens = token_get_all(file_get_contents($FileName));
+ $IsTestable = false;
+ $IsClass = false;
+
+ foreach ($Tokens as $Token) {
+ if (is_array($Token)) {
+ if (!$IsTestable && $Token[0] == T_DOC_COMMENT && strpos($Token[1], "@TestClass")) {
+ $IsTestable = true;
+ }
+ if ($IsTestable && $Token[0] == T_CLASS) {
+ $IsClass = true;
+ } else if ($IsClass && $Token[0] == T_STRING) {
+ $ReflectionClass = new ReflectionClass($Token[1]);
+ if (count(self::get_testable_methods($ReflectionClass))) {
+ self::$Classes[$Token[1]] = new ReflectionClass($Token[1]);
+ }
+ $IsTestable = false;
+ $IsClass = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if class exists in the map
+ */
+ public static function has_class($Class) {
+ return array_key_exists($Class, self::$Classes);
+ }
+
+ /**
+ * Checks if class has a given testable methood
+ */
+ public static function has_testable_method($Class, $Method) {
+ $TestableMethods = self::get_testable_methods($Class);
+ foreach($TestableMethods as $TestMethod) {
+ if ($TestMethod->getName() === $Method) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get testable methods in a class, a testable method has a @Test
+ */
+ public static function get_testable_methods($Class) {
+ if (is_string($Class)) {
+ $ReflectionClass = self::$Classes[$Class];
+ } else {
+ $ReflectionClass = $Class;
+ }
+ $ReflectionMethods = $ReflectionClass->getMethods();
+ $TestableMethods = array();
+ foreach($ReflectionMethods as $Method) {
+ if ($Method->isPublic() && $Method->isStatic() && strpos($Method->getDocComment(), "@Test")) {
+ $TestableMethods[] = $Method;
+ }
+ }
+ return $TestableMethods;
+ }
+
+
+ /**
+ * Get the class comment
+ */
+ public static function get_class_comment($Class) {
+ $ReflectionClass = self::$Classes[$Class];
+ return trim(str_replace(array("@TestClass", "*", "/"), "", $ReflectionClass->getDocComment()));
+ }
+
+ /**
+ * Get the undocumented methods in a class
+ */
+ public static function get_undocumented_methods($Class) {
+ $ReflectionClass = self::$Classes[$Class];
+ $Methods = array();
+ foreach($ReflectionClass->getMethods() as $Method) {
+ if (!$Method->getDocComment()) {
+ $Methods[] = $Method;
+ }
+ }
+ return $Methods;
+ }
+
+ /**
+ * Get the documented methods
+ */
+ public static function get_documented_methods($Class) {
+ $ReflectionClass = self::$Classes[$Class];
+ $Methods = array();
+ foreach($ReflectionClass->getMethods() as $Method) {
+ if ($Method->getDocComment()) {
+ $Methods[] = $Method;
+ }
+ }
+ return $Methods;
+ }
+
+ /**
+ * Get all methods in a class
+ */
+ public static function get_methods($Class) {
+ return self::$Classes[$Class]->getMethods();
+ }
+
+ /**
+ * Get a method comment
+ */
+ public static function get_method_comment($Method) {
+ return trim(str_replace(array("*", "/"), "", $Method->getDocComment()));
+ }
+
+}
\ No newline at end of file
diff --git a/classes/testingview.class.php b/classes/testingview.class.php
new file mode 100644
index 00000000..f5892dcb
--- /dev/null
+++ b/classes/testingview.class.php
@@ -0,0 +1,175 @@
+
+
+class TestingView {
+ /**
+ * Render the linkbox
+ */
+ public static function render_linkbox($Page) { ?>
+
=self::get_selected_link("Torrents", $Selected == "torrents")?>
-
=self::get_selected_link("Last.FM", $Selected == "lastfm")?>
+
=self::get_selected_link("Last.fm", $Selected == "lastfm")?>
=self::get_selected_link("Users", $Selected == "users")?>
=self::get_selected_link("Tags", $Selected == "tags")?>
=self::get_selected_link("Favorites", $Selected == "votes")?>
@@ -18,8 +18,8 @@ public static function render_linkbox($Selected) {
public static function render_artist_links($Selected, $View) {
?>
diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt
index 6e38da33..991b48c5 100644
--- a/docs/CHANGES.txt
+++ b/docs/CHANGES.txt
@@ -1,5 +1,8 @@
CHANGE LOG
+2014-01-23 by Ajax
+Introduced classes to test models. Classes with a @TestClass comment which include public static functions with a @Test comment will be made available to test via testing.php
+
2014-01-18 by alderaan
Fix grammar in pop-up notifications about new Staff PMs.
diff --git a/sections/testing/ajax_run_method.php b/sections/testing/ajax_run_method.php
new file mode 100644
index 00000000..a23930e1
--- /dev/null
+++ b/sections/testing/ajax_run_method.php
@@ -0,0 +1,18 @@
+
+authorize();
+if (!check_perms('users_mod')) {
+ error(404);
+}
+
+$Class = $_POST['class'];
+$Method = $_POST['method'];
+$Params = json_decode($_POST['params'], true);
+
+if (!empty($Class) && !empty($Method) && Testing::has_testable_method($Class, $Method)) {
+ if (count($Params)) {
+ $Results = call_user_func_array(array($Class, $Method), array_values($Params));
+ } else {
+ $Results = call_user_func(array($Class, $Method));
+ }
+ TestingView::render_results($Results);
+}
diff --git a/sections/testing/class.php b/sections/testing/class.php
new file mode 100644
index 00000000..85aea36a
--- /dev/null
+++ b/sections/testing/class.php
@@ -0,0 +1,26 @@
+
+if (!check_perms('users_mod')) {
+ error(404);
+}
+
+$Class = $_GET['name'];
+if (!empty($Class) && !Testing::has_class($Class)) {
+ error("Missing class");
+}
+
+View::show_header("Tests", "testing");
+
+?>
+
+
+
+ =TestingView::render_functions(Testing::get_testable_methods($Class));?>
+
+
+
+View::show_footer();
+
+
diff --git a/sections/testing/classes.php b/sections/testing/classes.php
new file mode 100644
index 00000000..57c1b369
--- /dev/null
+++ b/sections/testing/classes.php
@@ -0,0 +1,21 @@
+
+if (!check_perms('users_mod')) {
+ error(404);
+}
+
+View::show_header("Tests");
+
+?>
+
+
+
+ TestingView::render_classes(Testing::get_classes());?>
+
+
+
+View::show_footer();
+
+
diff --git a/sections/testing/comments.php b/sections/testing/comments.php
new file mode 100644
index 00000000..62d10c22
--- /dev/null
+++ b/sections/testing/comments.php
@@ -0,0 +1,22 @@
+
+
+if (!check_perms('users_mod')) {
+ error(404);
+}
+
+View::show_header("Tests");
+
+?>
+
+
+
+
+ TestingView::render_missing_documentation(Testing::get_classes());?>
+
+
+
+View::show_footer();
+
diff --git a/sections/testing/index.php b/sections/testing/index.php
new file mode 100644
index 00000000..1f750142
--- /dev/null
+++ b/sections/testing/index.php
@@ -0,0 +1,26 @@
+
+enforce_login();
+if (!check_perms('users_mod')) {
+ error(404);
+} else {
+ Testing::init();
+
+ if (!empty($_REQUEST['action'])) {
+ switch ($_REQUEST['action']) {
+ case 'class':
+ include(SERVER_ROOT.'/sections/testing/class.php');
+ break;
+ case 'ajax_run_method':
+ include(SERVER_ROOT.'/sections/testing/ajax_run_method.php');
+ break;
+ case 'comments':
+ include(SERVER_ROOT.'/sections/testing/comments.php');
+ break;
+ default:
+ include(SERVER_ROOT.'/sections/testing/classes.php');
+ break;
+ }
+ } else {
+ include(SERVER_ROOT.'/sections/testing/classes.php');
+ }
+}
diff --git a/sections/tools/tools.php b/sections/tools/tools.php
index ec99487c..ff9d783c 100644
--- a/sections/tools/tools.php
+++ b/sections/tools/tools.php
@@ -159,6 +159,7 @@ function create_row($Title, $URL, $HasPermission = false, $Tooltip = false) {
create_row("BBCode sandbox", "tools.php?action=bbcode_sandbox", check_perms("users_mod"));
create_row("Public sandbox", "tools.php?action=public_sandbox", check_perms("users_mod"), "Do not click this!");
create_row("Mod-level sandbox", "tools.php?action=mod_sandbox", check_perms("users_mod"), "Do not click this!");
+ create_row("Testing", "testing.php", check_perms("users_mod"));
if ($ToolsHTML) {
?>
diff --git a/sections/top10/lastfm.php b/sections/top10/lastfm.php
index 18a8ed79..dd194711 100644
--- a/sections/top10/lastfm.php
+++ b/sections/top10/lastfm.php
@@ -22,11 +22,11 @@
break;
}
-View::show_header("Last.FM", "jquery.imagesloaded,jquery.wookmark,top10", "tiles");
+View::show_header("Last.fm", "jquery.imagesloaded,jquery.wookmark,top10", "tiles");
?>
Top10View::render_artist_links($Category, $View); ?>
diff --git a/static/functions/testing.js b/static/functions/testing.js
new file mode 100644
index 00000000..c38f4cfd
--- /dev/null
+++ b/static/functions/testing.js
@@ -0,0 +1,31 @@
+$(document).ready(function() {
+ $(".run").click(function(e) {
+ e.preventDefault();
+ var id = $(this).data("gazelle-id");
+ var className = $(this).data("gazelle-class");
+ var methodName = $(this).data("gazelle-method");
+
+ var inputs = $("#method_params_" + id + " input");
+
+ var params = {};
+ $.each(inputs, function() {
+ params[this.name] = $(this).val();
+ });
+
+ $("#method_results_" + id).hide();
+ $.ajax({
+ type : "POST",
+ url : "testing.php?action=ajax_run_method",
+ data : {
+ "auth": authkey,
+ "class": className,
+ "method": methodName,
+ "params" : JSON.stringify(params)
+ }
+ }).done(function(response) {
+ $("#method_results_" + id).html(response);
+ $("#method_results_" + id).show();
+ });
+
+ });
+});
\ No newline at end of file
diff --git a/testing.php b/testing.php
new file mode 100644
index 00000000..172ee44a
--- /dev/null
+++ b/testing.php
@@ -0,0 +1,2 @@
+
+require('classes/script_start.php');