diff --git a/libs/ardour/test-env.sh b/libs/ardour/test-env.sh
new file mode 100644
index 0000000000..aa2cff9219
--- /dev/null
+++ b/libs/ardour/test-env.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Common libardour test env vars.
+#
+
+if [ ! -f './tempo.cc' ]; then
+ echo "This script must be run from within the libs/ardour directory";
+ exit 1;
+fi
+
+srcdir=`pwd`
+export ARDOUR_TEST_PATH=$srcdir/test/data
+cd ../../build
+
+libs='libs'
+
+export LD_LIBRARY_PATH=$libs/audiographer:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/timecode:$libs/libltc:/usr/local/lib:/usr/local/lib64:$LD_LIBRARY_PATH
+
+export ARDOUR_CONFIG_PATH=$top:$top/gtk2_ardour:$libs/..:$libs/../gtk2_ardour
+export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/panners/vbap
+export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie
+export ARDOUR_MCP_PATH="../mcp"
+export ARDOUR_DLL_PATH=$libs
+export ARDOUR_DATA_PATH=$top/gtk2_ardour:$top/build/gtk2_ardour:.
diff --git a/libs/ardour/test/data/2 Track-template/2 Track-template.template b/libs/ardour/test/data/2 Track-template/2 Track-template.template
new file mode 100644
index 0000000000..e43458ef24
--- /dev/null
+++ b/libs/ardour/test/data/2 Track-template/2 Track-template.template
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/ardour/test/session_test.cc b/libs/ardour/test/session_test.cc
new file mode 100644
index 0000000000..83273b5a2e
--- /dev/null
+++ b/libs/ardour/test/session_test.cc
@@ -0,0 +1,121 @@
+
+#include
+#include
+
+#include
+#include "midi++/manager.h"
+#include "pbd/textreceiver.h"
+#include "pbd/file_utils.h"
+#include "ardour/session.h"
+#include "ardour/audioengine.h"
+#include "ardour/smf_source.h"
+#include "ardour/midi_model.h"
+
+#include "test_common.h"
+
+#include "session_test.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SessionTest);
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+static TextReceiver text_receiver ("test");
+
+void
+SessionTest::setUp ()
+{
+ SessionEvent::create_per_thread_pool ("session_test", 512);
+
+ text_receiver.listen_to (error);
+ text_receiver.listen_to (info);
+ text_receiver.listen_to (fatal);
+ text_receiver.listen_to (warning);
+
+ // this is not a good singleton constructor pattern
+ AudioEngine* engine = 0;
+
+ try {
+ engine = new AudioEngine ("session_test", "");
+ } catch (const AudioEngine::NoBackendAvailable& engine_exception) {
+ cerr << engine_exception.what ();
+ }
+
+ CPPUNIT_ASSERT (engine);
+
+ init_post_engine ();
+
+ CPPUNIT_ASSERT (engine->start () == 0);
+}
+
+void
+SessionTest::tearDown ()
+{
+ // this is needed or there is a crash in MIDI::Manager::destroy
+ AudioEngine::instance()->stop (true);
+
+ MIDI::Manager::destroy ();
+ AudioEngine::destroy ();
+}
+
+void
+SessionTest::new_session ()
+{
+ const string session_name("test_session");
+ std::string new_session_dir = Glib::build_filename (new_test_output_dir(), session_name);
+
+ CPPUNIT_ASSERT (!Glib::file_test (new_session_dir, Glib::FILE_TEST_EXISTS));
+
+ Session* new_session = 0;
+
+ new_session = new Session (*AudioEngine::instance (), new_session_dir, session_name);
+
+ CPPUNIT_ASSERT (new_session);
+
+ // shouldn't need to do this as it is done in Session constructor
+ // via Session::when_engine_running
+ //AudioEngine::instance->set_session (new_session);
+
+ new_session->save_state ("");
+
+ delete new_session;
+}
+
+void
+SessionTest::new_session_from_template ()
+{
+ const string session_name("two_tracks");
+ const string session_template_dir_name("2 Track-template");
+
+ std::string new_session_dir = Glib::build_filename (new_test_output_dir(), session_name);
+
+ CPPUNIT_ASSERT (!Glib::file_test (new_session_dir, Glib::FILE_TEST_EXISTS));
+
+ std::string session_template_dir = test_search_path ().front ();
+ session_template_dir = Glib::build_filename (session_template_dir, "2 Track-template");
+
+ CPPUNIT_ASSERT (Glib::file_test (session_template_dir, Glib::FILE_TEST_IS_DIR));
+
+ Session* new_session = 0;
+ BusProfile* bus_profile = 0;
+
+ // create a new session based on session template
+ new_session = new Session (*AudioEngine::instance (), new_session_dir, session_name,
+ bus_profile, session_template_dir);
+
+ CPPUNIT_ASSERT (new_session);
+
+ new_session->save_state ("");
+
+ delete new_session;
+
+ Session* template_session = 0;
+
+ // reopen same session to check that it opens without error
+ template_session = new Session (*AudioEngine::instance (), new_session_dir, session_name);
+
+ CPPUNIT_ASSERT (template_session);
+
+ delete template_session;
+}
diff --git a/libs/ardour/test/session_test.h b/libs/ardour/test/session_test.h
new file mode 100644
index 0000000000..cadab67d3b
--- /dev/null
+++ b/libs/ardour/test/session_test.h
@@ -0,0 +1,18 @@
+
+#include
+#include
+
+class SessionTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SessionTest);
+ CPPUNIT_TEST (new_session);
+ CPPUNIT_TEST (new_session_from_template);
+ CPPUNIT_TEST_SUITE_END ();
+
+public:
+ virtual void setUp ();
+ virtual void tearDown ();
+
+ void new_session ();
+ void new_session_from_template ();
+};
diff --git a/libs/pbd/test/test_common.cc b/libs/pbd/test/test_common.cc
new file mode 100644
index 0000000000..16da3ed2a8
--- /dev/null
+++ b/libs/pbd/test/test_common.cc
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2011 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include
+
+#include "test_common.h"
+
+/**
+ * This allows tests to find the data files they require by looking
+ * in an installed location on windows or by setting an environment variable
+ * on unix.
+ */
+PBD::SearchPath
+test_search_path ()
+{
+#ifdef WIN32
+ std::string wsp(g_win32_get_package_installation_directory_of_module(NULL));
+ return Glib::build_filename (wsp, "pbd_testdata");
+#else
+ return Glib::getenv("PBD_TEST_PATH");
+#endif
+}
diff --git a/libs/pbd/test/test_common.h b/libs/pbd/test/test_common.h
new file mode 100644
index 0000000000..0dc62f61dc
--- /dev/null
+++ b/libs/pbd/test/test_common.h
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 2011 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef PBD_TEST_COMMON_H
+#define PBD_TEST_COMMON_H
+
+#include "pbd/search_path.h"
+
+PBD::SearchPath test_search_path ();
+
+#endif