[glom] Add test_selfhosting_new_from_example_operator.



commit 9c67be8dd6d161d5f78e162fc08d74c246a96470
Author: Murray Cumming <murrayc murrayc com>
Date:   Wed Feb 1 09:59:58 2012 +0100

    Add test_selfhosting_new_from_example_operator.
    
    * glom/base_db.[h|cc]: Move add_user() to
    * glom/libglom/db_utils.[h|cc]:
    * glom/frame_glom.cc:
    * glom/mode_design/users/dialog_users_list.cc: Adapted.
    
    * glom/libglom/connectionpool.cc: invalidate_connection():
    Also clear m_pFieldTypes to make sure that we refill it when making
    a new connection.
    
    * glom/libglom/data_structure/fieldtypes.[h|cc]: Add get_types_count().
    * tests/test_selfhosting_utils.[h|cc]: Add test_selfhost() to
    start an already-existing .glom system.
    * Makefile_tests.am:
    * tests/test_selfhosting_new_from_example_operator.cc:
    Add this new test which attempts to re-open a .glom system as
    a non-developer user. This currently fails to get the list of
    tables from the database. I must investigate that.

 ChangeLog                                          |   22 +++
 Makefile_tests.am                                  |    6 +
 glom/base_db.cc                                    |   48 +------
 glom/base_db.h                                     |    5 -
 glom/frame_glom.cc                                 |    4 +-
 glom/libglom/connectionpool.cc                     |   19 +++-
 glom/libglom/data_structure/field.cc               |    4 +
 glom/libglom/data_structure/fieldtypes.cc          |   16 ++
 glom/libglom/data_structure/fieldtypes.h           |    2 +
 glom/libglom/db_utils.cc                           |   73 +++++++++-
 glom/libglom/db_utils.h                            |    5 +
 glom/mode_design/users/dialog_users_list.cc        |    2 +-
 tests/.gitignore                                   |    1 +
 .../test_selfhosting_new_from_example_operator.cc  |  150 ++++++++++++++++++++
 tests/test_selfhosting_utils.cc                    |   54 +++++--
 tests/test_selfhosting_utils.h                     |   16 ++-
 16 files changed, 352 insertions(+), 75 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index e75d3ce..04e715d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2012-02-01  Murray Cumming  <murrayc murrayc com>
+
+	Add test_selfhosting_new_from_example_operator.
+
+	* glom/base_db.[h|cc]: Move add_user() to
+	* glom/libglom/db_utils.[h|cc]:
+	* glom/frame_glom.cc:
+	* glom/mode_design/users/dialog_users_list.cc: Adapted.
+
+	* glom/libglom/connectionpool.cc: invalidate_connection():
+	Also clear m_pFieldTypes to make sure that we refill it when making 
+	a new connection.
+
+	* glom/libglom/data_structure/fieldtypes.[h|cc]: Add get_types_count().
+	* tests/test_selfhosting_utils.[h|cc]: Add test_selfhost() to 
+	start an already-existing .glom system.
+	* Makefile_tests.am:
+	* tests/test_selfhosting_new_from_example_operator.cc:
+	Add this new test which attempts to re-open a .glom system as
+	a non-developer user. This currently fails to get the list of 
+	tables from the database. I must investigate that.
+
 2012-01-30  Murray Cumming  <murrayc murrayc com>
 
 	Add test_selfhosting_new_empty_change_sysprefs.
diff --git a/Makefile_tests.am b/Makefile_tests.am
index ff0b9d3..f350e9d 100644
--- a/Makefile_tests.am
+++ b/Makefile_tests.am
@@ -37,6 +37,7 @@ check_PROGRAMS =						\
 	tests/test_selfhosting_new_empty \
 	tests/test_selfhosting_new_empty_change_sysprefs \
 	tests/test_selfhosting_new_from_example \
+	tests/test_selfhosting_new_from_example_operator \
 	tests/test_selfhosting_new_from_example_strangepath \
 	tests/test_selfhosting_new_then_report \
 	tests/test_selfhosting_new_then_image \
@@ -71,6 +72,7 @@ TESTS =	tests/test_document_load	\
 	tests/test_selfhosting_new_empty \
 	tests/test_selfhosting_new_empty_change_sysprefs \
 	tests/test_selfhosting_new_from_example \
+	tests/test_selfhosting_new_from_example_operator \
 	tests/test_selfhosting_new_from_example_in_locales.sh \
 	tests/test_selfhosting_new_from_example_strangepath \
 	tests/test_selfhosting_new_then_report \
@@ -226,6 +228,10 @@ tests_test_selfhosting_new_from_example_SOURCES = tests/test_selfhosting_new_fro
 tests_test_selfhosting_new_from_example_LDADD = $(tests_ldadd)
 tests_test_selfhosting_new_from_example_CPPFLAGS = $(tests_cppflags)
 
+tests_test_selfhosting_new_from_example_operator_SOURCES = tests/test_selfhosting_new_from_example_operator.cc $(sources_test_selfhosting_utils)
+tests_test_selfhosting_new_from_example_operator_LDADD = $(tests_ldadd)
+tests_test_selfhosting_new_from_example_operator_CPPFLAGS = $(tests_cppflags)
+
 tests_test_selfhosting_new_from_example_strangepath_SOURCES = tests/test_selfhosting_new_from_example_strangepath.cc $(sources_test_selfhosting_utils)
 tests_test_selfhosting_new_from_example_strangepath_LDADD = $(tests_ldadd)
 tests_test_selfhosting_new_from_example_strangepath_CPPFLAGS = $(tests_cppflags)
diff --git a/glom/base_db.cc b/glom/base_db.cc
index be2ec2a..039d11f 100644
--- a/glom/base_db.cc
+++ b/glom/base_db.cc
@@ -1584,53 +1584,7 @@ void Base_DB::set_found_set_where_clause_for_portal(FoundSet& found_set, const s
   }
 }
 
-bool Base_DB::add_user(const Glib::ustring& user, const Glib::ustring& password, const Glib::ustring& group)
-{
-  if(user.empty() || password.empty() || group.empty())
-    return false;
-
-  //Create the user:
-  //Note that ' around the user fails, so we use ".
-  Glib::ustring strQuery = "CREATE USER " + DbUtils::escape_sql_id(user) + " PASSWORD '" + password + "'" ; //TODO: Escape the password.
-  if(group == GLOM_STANDARD_GROUP_NAME_DEVELOPER)
-    strQuery += " SUPERUSER CREATEDB CREATEROLE"; //Because SUPERUSER is not "inherited" from groups to members.
-
-  bool test = DbUtils::query_execute_string(strQuery);
-  if(!test)
-  {
-    std::cerr << G_STRFUNC << ": CREATE USER failed." << std::endl;
-    return false;
-  }
-
-  //Add it to the group:
-  strQuery = DbUtils::build_query_add_user_to_group(group, user);
-  test = DbUtils::query_execute_string(strQuery);
-  if(!test)
-  {
-    std::cerr << G_STRFUNC << ": ALTER GROUP failed." << std::endl;
-    return false;
-  }
-
-  //Remove any user rights, so that all rights come from the user's presence in the group:
-  Document* document = get_document();
-  if(!document)
-    return true;
-
-  Document::type_listTableInfo table_list = document->get_tables();
-
-  for(Document::type_listTableInfo::const_iterator iter = table_list.begin(); iter != table_list.end(); ++iter)
-  {
-    const Glib::ustring table_name = (*iter)->get_name();
-    const Glib::ustring strQuery = "REVOKE ALL PRIVILEGES ON " + DbUtils::escape_sql_id(table_name) + " FROM " + DbUtils::escape_sql_id(user);
-    const bool test = DbUtils::query_execute_string(strQuery);
-    if(!test)
-      std::cerr << G_STRFUNC << ": REVOKE failed." << std::endl;
-  }
-
-  return true;
-}
-
-
+//TODO: Move this into libglom and test it.
 bool Base_DB::remove_user(const Glib::ustring& user)
 {
   if(user.empty())
diff --git a/glom/base_db.h b/glom/base_db.h
index 7436460..36f9fcb 100644
--- a/glom/base_db.h
+++ b/glom/base_db.h
@@ -290,11 +290,6 @@ protected:
   typedef std::vector<Glib::ustring> type_vec_strings;
   static type_vec_strings util_vecStrings_from_Fields(const type_vec_fields& fields);
 
-  /** Add a @a user to the database, with the specified @a password, in the specified @a group.
-   * @result true if the addition succeeded.
-   */
-  bool add_user(const Glib::ustring& user, const Glib::ustring& password, const Glib::ustring& group);
-
   /** Remove the @a user from the database.
    * @result true if the removal succeeded.
    */
diff --git a/glom/frame_glom.cc b/glom/frame_glom.cc
index c36ce2b..1524d65 100644
--- a/glom/frame_glom.cc
+++ b/glom/frame_glom.cc
@@ -844,7 +844,7 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
         const bool initial_password_provided = connection_request_initial_password(user, password);
         bool added = false;
         if(initial_password_provided)
-          added = add_user(user, password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
+          added = DbUtils::add_user(document, user, password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
 
         if(initial_password_provided && added)
         {
@@ -942,7 +942,7 @@ void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>
         Glib::ustring default_password;
         const Glib::ustring default_user = Privs::get_default_developer_user_name(default_password);
 
-        const bool added = add_user(default_user, default_password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
+        const bool added = DbUtils::add_user(document, default_user, default_password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
         if(!added)
         {
            shared = true;
diff --git a/glom/libglom/connectionpool.cc b/glom/libglom/connectionpool.cc
index 4fa9298..cc29f1b 100644
--- a/glom/libglom/connectionpool.cc
+++ b/glom/libglom/connectionpool.cc
@@ -268,6 +268,8 @@ static bool on_connection_pool_cache_timeout()
 
 sharedptr<SharedConnection> ConnectionPool::connect()
 {
+  //std::cout << G_STRFUNC << ": debug" << std::endl;
+
   //Don't try to connect if we don't have a backend to connect to.
   g_return_val_if_fail(m_backend.get(), sharedptr<SharedConnection>(0));
 
@@ -340,11 +342,16 @@ sharedptr<SharedConnection> ConnectionPool::connect()
         {
           //update_meta_store_table_names() has been known to throw an exception.
           //Glom is mostly unusable when it fails, but that's still better than a crash.
-          m_refGdaConnection->update_meta_store_table_names(m_backend->get_public_schema_name());
+          //std::cout << G_STRFUNC << ": Before update_meta_store_table_name()" << std::endl;
+          const bool test = m_refGdaConnection->update_meta_store_table_names(m_backend->get_public_schema_name());
+          if(!test && !m_fake_connection)
+          {
+            std::cerr << G_STRFUNC << ": update_meta_store_table_names() failed without an exception." << std::endl;
+          }
         }
         catch(const Glib::Error& ex)
         {
-          //If the conneciton was not opened, because it is a fake connection,
+          //If the connection was not opened, because it is a fake connection,
           //then we should not be surprised that this fails,
           //and a warning will only be useful later when get_meta_store_data() fails when used in get_table_names_from_database().
           if(!m_fake_connection)
@@ -496,6 +503,10 @@ Glib::ustring ConnectionPool::get_database() const
 
 const FieldTypes* ConnectionPool::get_field_types() const
 {
+  //TODO: Investigate this:
+  //if(!m_pFieldTypes)
+  //  std::cerr << G_STRFUNC << ": m_pFieldTypes is null but this should never happen." << std::endl;
+
   return m_pFieldTypes;
 }
 
@@ -507,6 +518,7 @@ Gnome::Gda::SqlOperatorType ConnectionPool::get_string_find_operator() const
 
 void ConnectionPool::invalidate_connection()
 {
+  //std::cerr << G_STRFUNC << ": debug" << std::endl;
   connection_cached.clear();
   connection_cached_timeout_connection.disconnect();
   connection_cached_finished_connection.disconnect();
@@ -516,6 +528,9 @@ void ConnectionPool::invalidate_connection()
     
   m_refGdaConnection.reset();
   m_sharedconnection_refcount = 0;
+
+  delete m_pFieldTypes;
+  m_pFieldTypes = 0;
 }
 
 void ConnectionPool::on_sharedconnection_finished()
diff --git a/glom/libglom/data_structure/field.cc b/glom/libglom/data_structure/field.cc
index c91fbfb..af23c7d 100644
--- a/glom/libglom/data_structure/field.cc
+++ b/glom/libglom/data_structure/field.cc
@@ -554,6 +554,10 @@ Glib::ustring Field::get_sql_type() const
         const GType fieldType = m_field_info->get_g_type();
         strType = pFieldTypes->get_string_name_for_gdavaluetype(fieldType);
       }
+      else
+      {
+        std::cerr << G_STRFUNC << ": get_field_types() returned null" << std::endl;
+      }
     }
 
     if(strType == "unknowntype")
diff --git a/glom/libglom/data_structure/fieldtypes.cc b/glom/libglom/data_structure/fieldtypes.cc
index 82b539a..594b93f 100644
--- a/glom/libglom/data_structure/fieldtypes.cc
+++ b/glom/libglom/data_structure/fieldtypes.cc
@@ -82,6 +82,7 @@ FieldTypes::FieldTypes(const Glib::RefPtr<Gnome::Gda::Connection>& gda_connectio
             //std::cout << "debug: schema_type_string=" << schema_type_string << ", gda type=" << gdatype << "(" << g_type_name(gdatype) << ")" << std::endl;
 
             //Save it for later:
+            //std::cout << G_STRFUNC << ": debug: schema_type_string=" << schema_type_string << ", gdatype=" << g_type_name(gdatype) << std::endl;
             m_mapSchemaStringsToGdaTypes[schema_type_string] = gdatype;
 
             Glib::ustring gdatypestring = gda_g_type_to_string(gdatype); // TODO: What is this actually used for?
@@ -107,6 +108,21 @@ FieldTypes::~FieldTypes()
 {
 }
 
+guint FieldTypes::get_types_count() const
+{
+/*
+  if(!m_mapSchemaStringsToGdaTypes.empty())
+  {
+    const type_mapSchemaStringsToGdaTypes::const_iterator iter = m_mapSchemaStringsToGdaTypes.begin();
+    const Glib::ustring schema_type_string = iter->first;
+    const GType gdatype = iter->second;
+    std::cout << G_STRFUNC << ": debug: schema_type_string=" << schema_type_string << ", gdatype=" << g_type_name(gdatype) << std::endl;
+  }
+*/
+
+  return m_mapSchemaStringsToGdaTypes.size();
+}
+
 Glib::ustring FieldTypes::get_string_name_for_gdavaluetype(GType field_type) const
 {
   //Special-case gchararray (G_TYPE_STRING) because Gda reports this GType for several 
diff --git a/glom/libglom/data_structure/fieldtypes.h b/glom/libglom/data_structure/fieldtypes.h
index 4a765bf..6a47781 100644
--- a/glom/libglom/data_structure/fieldtypes.h
+++ b/glom/libglom/data_structure/fieldtypes.h
@@ -39,6 +39,8 @@ public:
   Glib::ustring get_string_name_for_gdavaluetype(GType field_type) const;
 
   GType get_fallback_type_for_gdavaluetype(GType field_type) const;
+
+  guint get_types_count() const;
   
 private:
   typedef std::map<Glib::ustring, GType> type_mapSchemaStringsToGdaTypes;
diff --git a/glom/libglom/db_utils.cc b/glom/libglom/db_utils.cc
index d7966bf..cffc9f9 100644
--- a/glom/libglom/db_utils.cc
+++ b/glom/libglom/db_utils.cc
@@ -246,8 +246,8 @@ bool recreate_database_from_document(Document* document, const sigc::slot<void>&
   sharedptr<SharedConnection> sharedconnection;
   try
   {
+    //Check that we can connect:
     sharedconnection = connection_pool->connect();
-      connection_pool->set_database(db_name); //The database was successfully created, so specify it when connecting from now on.
   }
   catch(const ExceptionConnection& ex)
   {
@@ -841,7 +841,7 @@ type_vec_fields get_fields_for_table_from_database(const Glib::ustring& table_na
     Glib::RefPtr<Gnome::Gda::DataModel> data_model_fields;
     try
     {
-      //This should work because we called update_meta_store_tables() in ConnectionPool,
+      //This should work because we called update_meta_store_table_names() in ConnectionPool,
       //and that gets the tables' fields too.
       data_model_fields = connection->get_meta_store_data(Gnome::Gda::CONNECTION_META_FIELDS, holder_list);
     }
@@ -2015,6 +2015,75 @@ Glib::ustring build_query_add_user_to_group(const Glib::ustring& group, const Gl
   return "ALTER GROUP " + escape_sql_id(group) + " ADD USER " + escape_sql_id(user);
 }
 
+static Glib::ustring build_query_add_user(const Glib::ustring& user, const Glib::ustring& password, bool superuser)
+{
+  if(user.empty())
+  {
+    std::cerr << G_STRFUNC << ": user is empty" << std::endl;
+  }
+
+  if(password.empty())
+  {
+    std::cerr << G_STRFUNC << ": password is empty" << std::endl;
+  }
+
+  //Note that ' around the user fails, so we use ".
+  Glib::ustring strQuery = "CREATE USER " + DbUtils::escape_sql_id(user) + " PASSWORD '" + password + "'" ; //TODO: Escape the password.
+  if(superuser)
+    strQuery += " SUPERUSER CREATEDB CREATEROLE"; //Because SUPERUSER is not "inherited" from groups to members.
+
+  return strQuery;
+}
+
+bool add_user(const Document* document, const Glib::ustring& user, const Glib::ustring& password, const Glib::ustring& group)
+{
+  if(!document)
+  {
+    std::cerr << G_STRFUNC << ": document is null." << std::endl;
+    return false;
+  }
+
+  if(user.empty() || password.empty() || group.empty())
+  {
+    std::cerr << G_STRFUNC << ": user, password, or group are empty." << std::endl;
+    return false;
+  }
+
+  //Create the user:
+  const bool superuser = (group == GLOM_STANDARD_GROUP_NAME_DEVELOPER);
+  const Glib::ustring query_add = build_query_add_user(user, password, superuser);
+  bool test = DbUtils::query_execute_string(query_add);
+  if(!test)
+  {
+    std::cerr << G_STRFUNC << ": CREATE USER failed." << std::endl;
+    return false;
+  }
+
+  //Add it to the group:
+  const Glib::ustring query_add_to_group = build_query_add_user_to_group(group, user);
+  test = DbUtils::query_execute_string(query_add_to_group);
+  if(!test)
+  {
+    std::cerr << G_STRFUNC << ": ALTER GROUP failed." << std::endl;
+    return false;
+  }
+
+  //Remove any user rights, so that all rights come from the user's presence in the group:
+  Document::type_listTableInfo table_list = document->get_tables();
+
+  for(Document::type_listTableInfo::const_iterator iter = table_list.begin(); iter != table_list.end(); ++iter)
+  {
+    const Glib::ustring table_name = (*iter)->get_name();
+    const Glib::ustring strQuery = "REVOKE ALL PRIVILEGES ON " + DbUtils::escape_sql_id(table_name) + " FROM " + DbUtils::escape_sql_id(user);
+    const bool test = DbUtils::query_execute_string(strQuery);
+    if(!test)
+      std::cerr << G_STRFUNC << ": REVOKE failed." << std::endl;
+  }
+
+  return true;
+}
+
+
 void set_fake_connection()
 {
   //Allow a fake connection, so sqlbuilder_get_full_query() can work:
diff --git a/glom/libglom/db_utils.h b/glom/libglom/db_utils.h
index 6b6dd83..66e7d36 100644
--- a/glom/libglom/db_utils.h
+++ b/glom/libglom/db_utils.h
@@ -179,6 +179,11 @@ Glib::ustring build_query_create_group(const Glib::ustring& group, bool superuse
 
 Glib::ustring build_query_add_user_to_group(const Glib::ustring& group, const Glib::ustring& user);
 
+/** Add a @a user to the database, with the specified @a password, in the specified @a group.
+ * @result true if the addition succeeded.
+ */
+bool add_user(const Document* document, const Glib::ustring& user, const Glib::ustring& password, const Glib::ustring& group);
+
 /** Get the value of the @a source_field from the @a relationship, using the @a key_value.
  */
 Gnome::Gda::Value get_lookup_value(Document* document, const Glib::ustring& table_name, const sharedptr<const Relationship>& relationship, const sharedptr<const Field>& source_field, const Gnome::Gda::Value & key_value);
diff --git a/glom/mode_design/users/dialog_users_list.cc b/glom/mode_design/users/dialog_users_list.cc
index 44ad9c2..31b7749 100644
--- a/glom/mode_design/users/dialog_users_list.cc
+++ b/glom/mode_design/users/dialog_users_list.cc
@@ -258,7 +258,7 @@ void Dialog_UsersList::on_button_user_new()
   if(response != Gtk::RESPONSE_OK)
     return;
 
-  add_user(user, password, m_combo_group->get_active_text() /* group */);
+  DbUtils::add_user(get_document(), user, password, m_combo_group->get_active_text() /* group */);
   fill_list();
 }
 
diff --git a/tests/.gitignore b/tests/.gitignore
index fbf004b..de7f842 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -19,6 +19,7 @@
 /test_selfhosting_new_empty
 /test_selfhosting_new_empty_change_sysprefs
 /test_selfhosting_new_from_example
+/test_selfhosting_new_from_example_operator
 /test_selfhosting_new_then_alter_table
 /test_selfhosting_new_then_backup_restore
 /test_selfhosting_new_then_get_privs
diff --git a/tests/test_selfhosting_new_from_example_operator.cc b/tests/test_selfhosting_new_from_example_operator.cc
new file mode 100644
index 0000000..a0e95e8
--- /dev/null
+++ b/tests/test_selfhosting_new_from_example_operator.cc
@@ -0,0 +1,150 @@
+/* Glom
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * 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
+71 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "tests/test_selfhosting_utils.h"
+#include <libglom/init.h>
+#include <libglom/utils.h>
+#include <libglom/db_utils.h>
+#include <libglom/connectionpool.h>
+#include <glib.h> //For g_assert()
+#include <iostream>
+#include <cstdlib> //For EXIT_SUCCESS and EXIT_FAILURE
+
+static bool test(Glom::Document::HostingMode hosting_mode)
+{
+  Glib::ustring temp_file_uri;
+  const Glib::ustring operator_user = "someoperator";
+  const Glib::ustring operator_password = "somepassword";
+
+  //Create and self-host the document:
+  {
+    Glom::Document document;
+    const bool recreated = 
+      test_create_and_selfhost_from_example("example_smallbusiness.glom", document, hosting_mode);
+    if(!recreated)
+    {
+      std::cerr << "Recreation failed." << std::endl;
+      return false;
+    }
+
+    temp_file_uri = test_get_temp_file_uri();
+    if(temp_file_uri.empty())
+    {
+      std::cerr << "temp_file_uri is empty." << std::endl;
+      return false;
+    }
+
+    //Add an operator user:
+    if(!Glom::DbUtils::add_user(&document, operator_user, operator_password, "personnel_department"))
+    {
+      std::cerr << "DbUtils::add_user() failed." << std::endl;
+      test_selfhosting_cleanup();
+      return false;
+    }
+
+    //Check that the developer user has access to database metadata:
+    const Glom::DbUtils::type_vec_strings tables = 
+      Glom::DbUtils::get_table_names_from_database(true /* ignore system tables */);
+    if(tables.empty())
+    {
+      std::cerr << "get_table_names_from_database() failed for developer user." << std::endl;
+      return false;
+    }
+
+    test_selfhosting_cleanup(false /* do not delete the file. */);
+  }
+  
+  //Self-host the document again, this time as operator:
+  {
+    Glom::Document document;
+    document.set_allow_autosave(false); //To simplify things and to not depend implicitly on autosave.
+    document.set_file_uri(temp_file_uri);
+    int failure_code = 0;
+    const bool test = document.load(failure_code);
+    //std::cout << "Document load result=" << test << std::endl;
+    if(!test)
+    {
+      std::cerr << "Document::load() failed with failure_code=" << failure_code << std::endl;
+      return false;
+    }
+
+    if(!test_selfhost(document, operator_user, operator_password))
+    {
+      std::cerr << "test_selfhost() failed." << std::endl;
+      return false;
+    }
+
+    //Check that the operator user still has access to database metadata:
+    Glom::ConnectionPool* connection_pool = Glom::ConnectionPool::get_instance();
+    connection_pool->connect();
+    const Glom::FieldTypes* field_types = connection_pool->get_field_types();
+    if(!field_types)
+    {
+      std::cerr << "get_field_types() returned null." << std::endl;
+      return false;
+    }
+
+    if(field_types->get_types_count() == 0)
+    {
+      std::cerr << "get_field_types() returned no types." << std::endl;
+      return false;
+    }
+
+    //std::cout << "field_types count=" << field_types->get_types_count() << std::endl;
+
+    const Glom::DbUtils::type_vec_strings tables = 
+      Glom::DbUtils::get_table_names_from_database(true /* ignore system tables */);
+    if(tables.empty())
+    {
+      std::cerr << "get_table_names_from_database() failed for operator user." << std::endl;
+      return false;
+    }
+
+    test_selfhosting_cleanup(); //Delete the file this time.
+  }
+ 
+  return true; 
+}
+
+int main()
+{
+  Glom::libglom_init();
+  
+  if(!test(Glom::Document::HOSTING_MODE_POSTGRES_SELF))
+  {
+    std::cerr << "Failed with PostgreSQL" << std::endl;
+    test_selfhosting_cleanup();
+    return EXIT_FAILURE;
+  }
+  
+  /* SQLite does not have user/group access levels,
+   * so the SQL queries woudl fail.
+  if(!test(Glom::Document::HOSTING_MODE_SQLITE))
+  {
+    std::cerr << "Failed with SQLite" << std::endl;
+    test_selfhosting_cleanup();
+    return EXIT_FAILURE;
+  }
+  */
+
+  Glom::libglom_deinit();
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/test_selfhosting_utils.cc b/tests/test_selfhosting_utils.cc
index 2fcaab2..f456adf 100644
--- a/tests/test_selfhosting_utils.cc
+++ b/tests/test_selfhosting_utils.cc
@@ -57,7 +57,8 @@ static void on_db_creation_progress()
   //std::cout << "Database creation progress" << std::endl;
 }
 
-std::string temp_filepath_dir;
+static std::string temp_filepath_dir; //Remembered so we can delete it later.
+static Glib::ustring temp_file_uri; //Rememered so we can return it sometimes.
 
 static bool check_directory_exists()
 {
@@ -115,13 +116,16 @@ static bool delete_directory(const std::string& uri)
   return delete_directory(file);
 }
 
-void test_selfhosting_cleanup()
+void test_selfhosting_cleanup(bool delete_file)
 {
   Glom::ConnectionPool* connection_pool = Glom::ConnectionPool::get_instance();
 
   const bool stopped = connection_pool->cleanup( sigc::ptr_fun(&on_cleanup_progress) );
   g_assert(stopped);
 
+  if(!delete_file)
+    return;
+
   //Make sure the directory is removed at the end:
   if(!temp_filepath_dir.empty())
   {
@@ -141,6 +145,29 @@ void test_selfhosting_cleanup()
   }
   
   temp_filepath_dir.clear();
+  temp_file_uri.clear(); //Forget this too.
+}
+
+bool test_selfhost(Glom::Document& document, const Glib::ustring& user, const Glib::ustring& password)
+{
+  //TODO: Let this happen automatically on first connection?
+  Glom::ConnectionPool* connection_pool = Glom::ConnectionPool::get_instance();
+
+  connection_pool->setup_from_document(&document);
+
+  connection_pool->set_user(user);
+  connection_pool->set_password(password);
+
+  const Glom::ConnectionPool::StartupErrors started = connection_pool->startup( sigc::ptr_fun(&on_startup_progress) );
+  if(started != Glom::ConnectionPool::Backend::STARTUPERROR_NONE)
+  {
+    std::cerr << "connection_pool->startup(): result=" << started << std::endl;
+    test_selfhosting_cleanup();
+    return false;
+  }
+  g_assert(started == Glom::ConnectionPool::Backend::STARTUPERROR_NONE);
+
+  return true;
 }
 
 bool test_create_and_selfhost_new_empty(Glom::Document& document, Glom::Document::HostingMode hosting_mode, const std::string& subdirectory_path)
@@ -168,9 +195,9 @@ bool test_create_and_selfhost_new_empty(Glom::Document& document, Glom::Document
   }
 
    //Save the example as a real file:
-  const Glib::ustring file_uri = Glib::filename_to_uri(temp_filepath);
+  temp_file_uri = Glib::filename_to_uri(temp_filepath);
   document.set_allow_autosave(false); //To simplify things and to not depend implicitly on autosave.
-  document.set_file_uri(file_uri);
+  document.set_file_uri(temp_file_uri);
 
   document.set_hosting_mode(hosting_mode);
   document.set_is_example_file(false);
@@ -195,20 +222,17 @@ bool test_create_and_selfhost_new_empty(Glom::Document& document, Glom::Document
   
   if(!check_directory_exists())
   {
-    std::cerr << "Failure: The data directory does not exist after calling initialize()." << std::endl; 
+    std::cerr << "Failure: The data directory does not exist after calling initialize()." << std::endl;
+    return false;
   }
 
   //Start self-hosting:
-  //TODO: Let this happen automatically on first connection?
-  const Glom::ConnectionPool::StartupErrors started = connection_pool->startup( sigc::ptr_fun(&on_startup_progress) );
-  if(started != Glom::ConnectionPool::Backend::STARTUPERROR_NONE)
-  {
-    std::cerr << "connection_pool->startup(): result=" << started << std::endl;
-    test_selfhosting_cleanup();
-  }
-  g_assert(started == Glom::ConnectionPool::Backend::STARTUPERROR_NONE);
+  return test_selfhost(document, user, password);
+}
 
-  return true;
+Glib::ustring test_get_temp_file_uri()
+{
+  return temp_file_uri;
 }
 
 bool test_create_and_selfhost_new_database(Glom::Document& document, Glom::Document::HostingMode hosting_mode, const Glib::ustring& database_name, const std::string& subdirectory_path)
@@ -219,7 +243,7 @@ bool test_create_and_selfhost_new_database(Glom::Document& document, Glom::Docum
     return false;
   } 
 
-  const Glib::ustring db_name = Glom::DbUtils::get_unused_database_name("test_db");
+  const Glib::ustring db_name = Glom::DbUtils::get_unused_database_name(database_name);
   if(db_name.empty())
   {
     std::cerr << "DbUtils::get_unused_database_name) failed." << std::endl;
diff --git a/tests/test_selfhosting_utils.h b/tests/test_selfhosting_utils.h
index a31c5f4..0233198 100644
--- a/tests/test_selfhosting_utils.h
+++ b/tests/test_selfhosting_utils.h
@@ -56,10 +56,24 @@ bool test_create_and_selfhost_from_example(const std::string& example_filename,
  */
 bool test_create_and_selfhost_from_uri(const Glib::ustring& file_uri, Glom::Document& document, Glom::Document::HostingMode hosting_mode, const std::string& subdirectory_path = std::string());
 
+/** Start self-hosting of a .glom document.
+ * @param document The document must already be saved to a file.
+ */
+bool test_selfhost(Glom::Document& document, const Glib::ustring& user, const Glib::ustring& password);
+
+
 bool test_model_expected_size(const Glib::RefPtr<Gnome::Gda::DataModel>& data_model, guint columns_count, guint rows_count);
 bool test_table_exists(const Glib::ustring& table_name, const Glom::Document& document);
 
-void test_selfhosting_cleanup();
+/** Return the URI of the temporary .glom file created by the test_create_and_selfhost_*() methods.
+ * This should only be used by some special tests.
+ */
+Glib::ustring test_get_temp_file_uri();
+
+/** Stop the self-hosting server process,
+ * and (optionally) delete the temporary .glom file and its data.
+ */
+void test_selfhosting_cleanup(bool delete_file = true);
 
 bool test_example_musiccollection_data(const Glom::Document* document);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]