diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index 0fce75c8369bc..14cda7d62c35d 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -51,6 +51,7 @@ struct TestCtxt<'a> {
     ext_cx: ExtCtxt<'a>,
     testfns: Vec<Test>,
     reexport_mod_ident: ast::Ident,
+    reexport_test_harness_main: Option<InternedString>,
     is_test_crate: bool,
     config: ast::CrateConfig,
 }
@@ -64,8 +65,16 @@ pub fn modify_for_testing(sess: &Session,
     // command line options.
     let should_test = attr::contains_name(krate.config.as_slice(), "test");
 
+    // Check for #[reexport_test_harness_main = "some_name"] which
+    // creates a `use some_name = __test::main;`. This needs to be
+    // unconditional, so that the attribute is still marked as used in
+    // non-test builds.
+    let reexport_test_harness_main =
+        attr::first_attr_value_str_by_name(krate.attrs.as_slice(),
+                                           "reexport_test_harness_main");
+
     if should_test {
-        generate_test_harness(sess, krate)
+        generate_test_harness(sess, reexport_test_harness_main, krate)
     } else {
         strip_test_functions(krate)
     }
@@ -79,14 +88,17 @@ struct TestHarnessGenerator<'a> {
 
 impl<'a> fold::Folder for TestHarnessGenerator<'a> {
     fn fold_crate(&mut self, c: ast::Crate) -> ast::Crate {
-        let folded = fold::noop_fold_crate(c, self);
+        let mut folded = fold::noop_fold_crate(c, self);
 
         // Add a special __test module to the crate that will contain code
         // generated for the test harness
-        ast::Crate {
-            module: add_test_module(&self.cx, &folded.module),
-            .. folded
+        let (mod_, reexport) = mk_test_module(&self.cx, &self.cx.reexport_test_harness_main);
+        folded.module.items.push(mod_);
+        match reexport {
+            Some(re) => folded.module.view_items.push(re),
+            None => {}
         }
+        folded
     }
 
     fn fold_item(&mut self, i: Gc<ast::Item>) -> SmallVector<Gc<ast::Item>> {
@@ -196,7 +208,9 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec<ast::Ident>,
     }
 }
 
-fn generate_test_harness(sess: &Session, krate: ast::Crate) -> ast::Crate {
+fn generate_test_harness(sess: &Session,
+                         reexport_test_harness_main: Option<InternedString>,
+                         krate: ast::Crate) -> ast::Crate {
     let mut cx: TestCtxt = TestCtxt {
         sess: sess,
         ext_cx: ExtCtxt::new(&sess.parse_sess, sess.opts.cfg.clone(),
@@ -206,7 +220,8 @@ fn generate_test_harness(sess: &Session, krate: ast::Crate) -> ast::Crate {
                              }),
         path: Vec::new(),
         testfns: Vec::new(),
-        reexport_mod_ident: token::str_to_ident("__test_reexports"),
+        reexport_mod_ident: token::gensym_ident("__test_reexports"),
+        reexport_test_harness_main: reexport_test_harness_main,
         is_test_crate: is_test_crate(&krate),
         config: krate.config.clone(),
     };
@@ -314,14 +329,6 @@ fn should_fail(i: Gc<ast::Item>) -> bool {
     attr::contains_name(i.attrs.as_slice(), "should_fail")
 }
 
-fn add_test_module(cx: &TestCtxt, m: &ast::Mod) -> ast::Mod {
-    let testmod = mk_test_module(cx);
-    ast::Mod {
-        items: m.items.clone().append_one(testmod),
-        ..(*m).clone()
-    }
-}
-
 /*
 
 We're going to be building a module that looks more or less like:
@@ -359,7 +366,8 @@ fn mk_std(cx: &TestCtxt) -> ast::ViewItem {
     }
 }
 
-fn mk_test_module(cx: &TestCtxt) -> Gc<ast::Item> {
+fn mk_test_module(cx: &TestCtxt, reexport_test_harness_main: &Option<InternedString>)
+                  -> (Gc<ast::Item>, Option<ast::ViewItem>) {
     // Link to test crate
     let view_items = vec!(mk_std(cx));
 
@@ -383,18 +391,35 @@ fn mk_test_module(cx: &TestCtxt) -> Gc<ast::Item> {
     };
     let item_ = ast::ItemMod(testmod);
 
+    let mod_ident = token::gensym_ident("__test");
     let item = ast::Item {
-        ident: token::str_to_ident("__test"),
+        ident: mod_ident,
         attrs: Vec::new(),
         id: ast::DUMMY_NODE_ID,
         node: item_,
         vis: ast::Public,
         span: DUMMY_SP,
-     };
+    };
+    let reexport = reexport_test_harness_main.as_ref().map(|s| {
+        // building `use <ident> = __test::main`
+        let reexport_ident = token::str_to_ident(s.get());
+
+        let use_path =
+            nospan(ast::ViewPathSimple(reexport_ident,
+                                       path_node(vec![mod_ident, token::str_to_ident("main")]),
+                                       ast::DUMMY_NODE_ID));
+
+        ast::ViewItem {
+            node: ast::ViewItemUse(box(GC) use_path),
+            attrs: vec![],
+            vis: ast::Inherited,
+            span: DUMMY_SP
+        }
+    });
 
     debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item));
 
-    box(GC) item
+    (box(GC) item, reexport)
 }
 
 fn nospan<T>(t: T) -> codemap::Spanned<T> {
@@ -417,11 +442,27 @@ fn mk_tests(cx: &TestCtxt) -> Gc<ast::Item> {
     // The vector of test_descs for this crate
     let test_descs = mk_test_descs(cx);
 
-    (quote_item!(&cx.ext_cx,
-        pub static TESTS : &'static [self::test::TestDescAndFn] =
-            $test_descs
-        ;
-    )).unwrap()
+    // FIXME #15962: should be using quote_item, but that stringifies
+    // __test_reexports, causing it to be reinterned, losing the
+    // gensym information.
+    let sp = DUMMY_SP;
+    let ecx = &cx.ext_cx;
+    let struct_type = ecx.ty_path(ecx.path(sp, vec![ecx.ident_of("self"),
+                                                    ecx.ident_of("test"),
+                                                    ecx.ident_of("TestDescAndFn")]),
+                                  None);
+    let static_lt = ecx.lifetime(sp, token::special_idents::static_lifetime.name);
+    // &'static [self::test::TestDescAndFn]
+    let static_type = ecx.ty_rptr(sp,
+                                  ecx.ty(sp, ast::TyVec(struct_type)),
+                                  Some(static_lt),
+                                  ast::MutImmutable);
+    // static TESTS: $static_type = &[...];
+    ecx.item_static(sp,
+                    ecx.ident_of("TESTS"),
+                    static_type,
+                    ast::MutImmutable,
+                    test_descs)
 }
 
 fn is_test_crate(krate: &ast::Crate) -> bool {
@@ -448,59 +489,58 @@ fn mk_test_descs(cx: &TestCtxt) -> Gc<ast::Expr> {
 }
 
 fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> Gc<ast::Expr> {
+    // FIXME #15962: should be using quote_expr, but that stringifies
+    // __test_reexports, causing it to be reinterned, losing the
+    // gensym information.
+
     let span = test.span;
     let path = test.path.clone();
+    let ecx = &cx.ext_cx;
+    let self_id = ecx.ident_of("self");
+    let test_id = ecx.ident_of("test");
+
+    // creates self::test::$name
+    let test_path = |name| {
+        ecx.path(span, vec![self_id, test_id, ecx.ident_of(name)])
+    };
+    // creates $name: $expr
+    let field = |name, expr| ecx.field_imm(span, ecx.ident_of(name), expr);
 
     debug!("encoding {}", ast_util::path_name_i(path.as_slice()));
 
-    let name_lit: ast::Lit =
-        nospan(ast::LitStr(token::intern_and_get_ident(
-                    ast_util::path_name_i(path.as_slice()).as_slice()),
-                    ast::CookedStr));
+    // path to the #[test] function: "foo::bar::baz"
+    let path_string = ast_util::path_name_i(path.as_slice());
+    let name_expr = ecx.expr_str(span, token::intern_and_get_ident(path_string.as_slice()));
 
-    let name_expr = box(GC) ast::Expr {
-          id: ast::DUMMY_NODE_ID,
-          node: ast::ExprLit(box(GC) name_lit),
-          span: span
-    };
+    // self::test::StaticTestName($name_expr)
+    let name_expr = ecx.expr_call(span,
+                                  ecx.expr_path(test_path("StaticTestName")),
+                                  vec![name_expr]);
 
-    let mut visible_path = vec![cx.reexport_mod_ident.clone()];
-    visible_path.extend(path.move_iter());
-    let fn_path = cx.ext_cx.path_global(DUMMY_SP, visible_path);
+    let ignore_expr = ecx.expr_bool(span, test.ignore);
+    let fail_expr = ecx.expr_bool(span, test.should_fail);
 
-    let fn_expr = box(GC) ast::Expr {
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ExprPath(fn_path),
-        span: span,
-    };
+    // self::test::TestDesc { ... }
+    let desc_expr = ecx.expr_struct(
+        span,
+        test_path("TestDesc"),
+        vec![field("name", name_expr),
+             field("ignore", ignore_expr),
+             field("should_fail", fail_expr)]);
 
-    let t_expr = if test.bench {
-        quote_expr!(&cx.ext_cx, self::test::StaticBenchFn($fn_expr) )
-    } else {
-        quote_expr!(&cx.ext_cx, self::test::StaticTestFn($fn_expr) )
-    };
 
-    let ignore_expr = if test.ignore {
-        quote_expr!(&cx.ext_cx, true )
-    } else {
-        quote_expr!(&cx.ext_cx, false )
-    };
+    let mut visible_path = vec![cx.reexport_mod_ident.clone()];
+    visible_path.extend(path.move_iter());
 
-    let fail_expr = if test.should_fail {
-        quote_expr!(&cx.ext_cx, true )
-    } else {
-        quote_expr!(&cx.ext_cx, false )
-    };
+    let fn_expr = ecx.expr_path(ecx.path_global(span, visible_path));
 
-    let e = quote_expr!(&cx.ext_cx,
-        self::test::TestDescAndFn {
-            desc: self::test::TestDesc {
-                name: self::test::StaticTestName($name_expr),
-                ignore: $ignore_expr,
-                should_fail: $fail_expr
-            },
-            testfn: $t_expr,
-        }
-    );
-    e
+    let variant_name = if test.bench { "StaticBenchFn" } else { "StaticTestFn" };
+    // self::test::$variant_name($fn_expr)
+    let testfn_expr = ecx.expr_call(span, ecx.expr_path(test_path(variant_name)), vec![fn_expr]);
+
+    // self::test::TestDescAndFn { ... }
+    ecx.expr_struct(span,
+                    test_path("TestDescAndFn"),
+                    vec![field("desc", desc_expr),
+                         field("testfn", testfn_expr)])
 }
diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs
index 24b8c29785804..dd80ab3ee78a1 100644
--- a/src/librustuv/lib.rs
+++ b/src/librustuv/lib.rs
@@ -48,6 +48,8 @@ via `close` and `delete` methods.
 #![deny(unused_result, unused_must_use)]
 #![allow(visible_private_types)]
 
+#![reexport_test_harness_main = "test_main"]
+
 #[cfg(test)] extern crate green;
 #[cfg(test)] extern crate debug;
 #[cfg(test)] extern crate realrustuv = "rustuv";
@@ -76,13 +78,9 @@ pub use self::timer::TimerWatcher;
 pub use self::tty::TtyWatcher;
 
 // Run tests with libgreen instead of libnative.
-//
-// FIXME: This egregiously hacks around starting the test runner in a different
-//        threading mode than the default by reaching into the auto-generated
-//        '__test' module.
 #[cfg(test)] #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
-    green::start(argc, argv, event_loop, __test::main)
+    green::start(argc, argv, event_loop, test_main)
 }
 
 mod macros;
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 125c3fdf5d90c..20fc7efeb574f 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -114,6 +114,8 @@
 #![allow(deprecated)]
 #![deny(missing_doc)]
 
+#![reexport_test_harness_main = "test_main"]
+
 // When testing libstd, bring in libuv as the I/O backend so tests can print
 // things and all of the std::io tests have an I/O interface to run on top
 // of
@@ -186,13 +188,9 @@ pub use unicode::char;
 pub use core_sync::comm;
 
 // Run tests with libgreen instead of libnative.
-//
-// FIXME: This egregiously hacks around starting the test runner in a different
-//        threading mode than the default by reaching into the auto-generated
-//        '__test' module.
 #[cfg(test)] #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
-    green::start(argc, argv, rustuv::event_loop, __test::main)
+    green::start(argc, argv, rustuv::event_loop, test_main)
 }
 
 /* Exported macros */
diff --git a/src/test/compile-fail/inaccessible-test-modules.rs b/src/test/compile-fail/inaccessible-test-modules.rs
new file mode 100644
index 0000000000000..b646f8083b8d5
--- /dev/null
+++ b/src/test/compile-fail/inaccessible-test-modules.rs
@@ -0,0 +1,19 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags:--test
+
+// the `--test` harness creates modules with these textual names, but
+// they should be inaccessible from normal code.
+use x = __test; //~ ERROR unresolved import `__test`
+use y = __test_reexports; //~ ERROR unresolved import `__test_reexports`
+
+#[test]
+fn baz() {}
diff --git a/src/test/run-make/test-harness/Makefile b/src/test/run-make/test-harness/Makefile
new file mode 100644
index 0000000000000..4517af8e24be5
--- /dev/null
+++ b/src/test/run-make/test-harness/Makefile
@@ -0,0 +1,7 @@
+-include ../tools.mk
+
+all:
+	# check that #[ignore(cfg(...))] does the right thing.
+	$(RUSTC) --test test-ignore-cfg.rs --cfg ignorecfg
+	$(call RUN,test-ignore-cfg) | grep 'shouldnotignore ... ok'
+	$(call RUN,test-ignore-cfg) | grep 'shouldignore ... ignored'
diff --git a/src/test/run-make/test-harness/test-ignore-cfg.rs b/src/test/run-make/test-harness/test-ignore-cfg.rs
new file mode 100644
index 0000000000000..a8f88cc8544a9
--- /dev/null
+++ b/src/test/run-make/test-harness/test-ignore-cfg.rs
@@ -0,0 +1,19 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[test]
+#[ignore(cfg(ignorecfg))]
+fn shouldignore() {
+}
+
+#[test]
+#[ignore(cfg(noignorecfg))]
+fn shouldnotignore() {
+}
diff --git a/src/test/run-pass/core-run-destroy.rs b/src/test/run-pass/core-run-destroy.rs
index 8e84278c10e02..d187a6a8afebb 100644
--- a/src/test/run-pass/core-run-destroy.rs
+++ b/src/test/run-pass/core-run-destroy.rs
@@ -16,6 +16,8 @@
 // instead of in std.
 
 #![feature(macro_rules)]
+#![reexport_test_harness_main = "test_main"]
+
 extern crate libc;
 
 extern crate native;
@@ -55,7 +57,7 @@ macro_rules! iotest (
 
 #[cfg(test)] #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
-    green::start(argc, argv, rustuv::event_loop, __test::main)
+    green::start(argc, argv, rustuv::event_loop, test_main)
 }
 
 iotest!(fn test_destroy_once() {
diff --git a/src/test/run-pass/reexport-test-harness-main.rs b/src/test/run-pass/reexport-test-harness-main.rs
new file mode 100644
index 0000000000000..309ae1bcc56ec
--- /dev/null
+++ b/src/test/run-pass/reexport-test-harness-main.rs
@@ -0,0 +1,21 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-pretty
+// compile-flags:--test
+
+#![reexport_test_harness_main = "test_main"]
+
+#[cfg(test)]
+fn _unused() {
+    // should resolve to the entry point function the --test harness
+    // creates.
+    test_main();
+}
diff --git a/src/test/run-pass/tcp-connect-timeouts.rs b/src/test/run-pass/tcp-connect-timeouts.rs
index d2408509fc582..6f6fff15814d5 100644
--- a/src/test/run-pass/tcp-connect-timeouts.rs
+++ b/src/test/run-pass/tcp-connect-timeouts.rs
@@ -18,6 +18,7 @@
 
 #![feature(macro_rules, globs)]
 #![allow(experimental)]
+#![reexport_test_harness_main = "test_main"]
 
 extern crate native;
 extern crate green;
@@ -25,7 +26,7 @@ extern crate rustuv;
 
 #[cfg(test)] #[start]
 fn start(argc: int, argv: *const *const u8) -> int {
-    green::start(argc, argv, rustuv::event_loop, __test::main)
+    green::start(argc, argv, rustuv::event_loop, test_main)
 }
 
 macro_rules! iotest (
diff --git a/src/test/run-pass/test-ignore-cfg.rs b/src/test/run-pass/test-ignore-cfg.rs
deleted file mode 100644
index b36fbca2da048..0000000000000
--- a/src/test/run-pass/test-ignore-cfg.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// compile-flags: --test --cfg ignorecfg
-// ignore-pretty: does not work well with `--test`
-
-#[test]
-#[ignore(cfg(ignorecfg))]
-fn shouldignore() {
-}
-
-#[test]
-#[ignore(cfg(noignorecfg))]
-fn shouldnotignore() {
-}
-
-#[test]
-fn checktests() {
-    // Pull the tests out of the secreturn test module
-    let tests = __test::TESTS;
-
-    assert!(
-        tests.iter().any(|t| t.desc.name.to_string().as_slice() == "shouldignore" &&
-                         t.desc.ignore));
-
-    assert!(
-        tests.iter().any(|t| t.desc.name.to_string().as_slice() == "shouldnotignore" &&
-                         !t.desc.ignore));
-}