Example #1
0
 def test_get_object_default_values_is_valid(self):
     """Checks that the default values provided by get_default_values()
     correspond to the ones defined in objects.py.
     """
     object_default_vals = object_registry.get_default_object_values()
     all_object_classes = object_registry.Registry.get_all_object_classes()
     for (obj_type, default_value) in object_default_vals.items():
         self.assertIn(obj_type, all_object_classes)
         self.assertEqual(default_value,
                          all_object_classes[obj_type].default_value)
Example #2
0
    def test_all_rule_input_fields_have_default_values(self):
        """Checks that all rule input fields have a default value, and this
        is provided in get_default_values().
        """
        interactions = interaction_registry.Registry.get_all_interactions()
        object_default_vals = object_registry.get_default_object_values()

        for interaction in interactions:
            for rule_name in interaction.rules_dict:
                param_list = interaction.get_rule_param_list(rule_name)

                for (_, param_obj_type) in param_list:
                    param_obj_type_name = param_obj_type.__name__
                    default_value = param_obj_type.default_value
                    self.assertIsNotNone(
                        default_value,
                        msg=(
                            'No default value specified for object class %s.' %
                            param_obj_type_name))
                    self.assertIn(param_obj_type_name, object_default_vals)
                    self.assertEqual(default_value,
                                     object_default_vals[param_obj_type_name])
Example #3
0
    def test_default_interactions_are_valid(self):
        """Test that the default interactions are valid."""

        all_interaction_ids = (
            interaction_registry.Registry.get_all_interaction_ids())
        for interaction_id in all_interaction_ids:

            # Check that the interaction id is valid.
            self.assertTrue(self._is_camel_cased(interaction_id))
            hyphenated_interaction_id = (
                utils.camelcase_to_hyphenated(interaction_id))

            # Check that the interaction directory exists.
            interaction_dir = os.path.join(feconf.INTERACTIONS_DIR,
                                           interaction_id)
            self.assertTrue(os.path.isdir(interaction_dir))

            # The interaction directory should contain the following files:
            #  Required:
            #    * A python file called {InteractionName}.py.
            #    * An __init__.py file used to import the Python file.
            #    * A TypeScript file called {InteractionName}.ts.
            #    * If migrated to Angular2+, a module.ts file called
            #       {InteractionName}-interactions.module.ts
            #    * A directory name 'directives' containing TS and HTML files
            #      for directives
            #    * A directory named 'static' containing at least a .png file.
            #  Optional:
            #    * A JS file called protractor.js.
            interaction_dir_contents = (
                self._listdir_omit_ignored(interaction_dir))

            interaction_dir_optional_dirs_and_files_count = 0

            try:
                self.assertTrue(
                    os.path.isfile(
                        os.path.join(interaction_dir, 'protractor.js')))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            try:
                self.assertTrue(
                    os.path.isfile(
                        os.path.join(
                            interaction_dir, '%s-interactions.module.ts' %
                            hyphenated_interaction_id)))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            try:
                self.assertTrue(
                    os.path.isfile(
                        os.path.join(
                            interaction_dir, '%s-prediction.service.ts' %
                            hyphenated_interaction_id)))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            try:
                self.assertTrue(
                    os.path.isfile(
                        os.path.join(
                            interaction_dir, '%s-prediction.service.spec.ts' %
                            hyphenated_interaction_id)))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            self.assertEqual(interaction_dir_optional_dirs_and_files_count + 5,
                             len(interaction_dir_contents))

            py_file = os.path.join(interaction_dir, '%s.py' % interaction_id)
            ts_file = os.path.join(interaction_dir, '%s.ts' % interaction_id)

            self.assertTrue(os.path.isfile(py_file))
            self.assertTrue(os.path.isfile(ts_file))

            # Check that __init__.py file exists.
            init_file = os.path.join(interaction_dir, '__init__.py')
            self.assertTrue(os.path.isfile(init_file))

            # Check that the directives subdirectory exists.
            directives_dir = os.path.join(interaction_dir, 'directives')
            self.assertTrue(os.path.isdir(directives_dir))

            # The directives directory should contain the following files:
            #  Required:
            #    * A TS file called
            #    oppia-interactive-{InteractionName}.directive.ts.
            #    * A TS file called OppiaResponse{InteractionName}.directive.ts.
            #    * A TS file called
            #    oppia-short-response-{InteractionName}.directive.ts.
            #    * A TS file called {InteractionName}-rules.service.ts.
            #    * A TS file called {InteractionName}-validation.service.ts.
            #    * A HTML file called
            #      {InteractionName}-interaction.directive.html.
            #    * A HTML file called
            #      {InteractionName}-response.directive.html.
            #    * A HTML file called
            #      {InteractionName}-short-response.directive.html.
            #  Optional:
            #    * A TS file called
            #      {InteractionName}-validation.service.specs.ts.
            #    * A TS file called {InteractionName}-rules.service.specs.ts.

            hyphenated_interaction_id = (
                utils.camelcase_to_hyphenated(interaction_id))
            if interaction_id in INTERACTIONS_THAT_USE_COMPONENTS:
                interaction_ts_file = os.path.join(
                    directives_dir, 'oppia-interactive-%s.component.ts' %
                    (hyphenated_interaction_id))
                response_ts_file = os.path.join(
                    directives_dir, 'oppia-response-%s.component.ts' %
                    hyphenated_interaction_id)
                short_response_ts_file = os.path.join(
                    directives_dir, 'oppia-short-response-%s.component.ts' %
                    (hyphenated_interaction_id))
                rules_service_ts_file = os.path.join(
                    directives_dir,
                    '%s-rules.service.ts' % hyphenated_interaction_id)
                validation_service_ts_file = os.path.join(
                    directives_dir,
                    '%s-validation.service.ts' % hyphenated_interaction_id)
                interaction_html = os.path.join(
                    directives_dir, '%s-interaction.component.html' %
                    hyphenated_interaction_id)
                response_html = os.path.join(
                    directives_dir,
                    '%s-response.component.html' % hyphenated_interaction_id)
                short_response_html = os.path.join(
                    directives_dir, '%s-short-response.component.html' %
                    hyphenated_interaction_id)
            else:
                interaction_ts_file = os.path.join(
                    directives_dir, 'oppia-interactive-%s.directive.ts' %
                    (hyphenated_interaction_id))
                response_ts_file = os.path.join(
                    directives_dir, 'oppia-response-%s.directive.ts' %
                    hyphenated_interaction_id)
                short_response_ts_file = os.path.join(
                    directives_dir, 'oppia-short-response-%s.directive.ts' %
                    (hyphenated_interaction_id))
                rules_service_ts_file = os.path.join(
                    directives_dir,
                    '%s-rules.service.ts' % hyphenated_interaction_id)
                validation_service_ts_file = os.path.join(
                    directives_dir,
                    '%s-validation.service.ts' % hyphenated_interaction_id)
                interaction_html = os.path.join(
                    directives_dir, '%s-interaction.directive.html' %
                    hyphenated_interaction_id)
                response_html = os.path.join(
                    directives_dir,
                    '%s-response.directive.html' % hyphenated_interaction_id)
                short_response_html = os.path.join(
                    directives_dir, '%s-short-response.directive.html' %
                    hyphenated_interaction_id)

            self.assertTrue(os.path.isfile(interaction_ts_file))
            self.assertTrue(os.path.isfile(response_ts_file))
            self.assertTrue(os.path.isfile(short_response_ts_file))
            self.assertTrue(os.path.isfile(interaction_html))
            self.assertTrue(os.path.isfile(response_html))
            self.assertTrue(os.path.isfile(short_response_html))
            self.assertTrue(os.path.isfile(rules_service_ts_file))
            self.assertTrue(os.path.isfile(validation_service_ts_file))

            # Check that the PNG thumbnail image has the correct dimensions.
            static_dir = os.path.join(interaction_dir, 'static')
            self.assertTrue(os.path.isdir(static_dir))
            png_file = os.path.join(interaction_dir, 'static',
                                    '%s.png' % interaction_id)
            self.assertTrue(os.path.isfile(png_file))
            with python_utils.open_file(png_file, 'rb', encoding=None) as f:
                img_data = f.read()
                width, height = struct.unpack('>LL', img_data[16:24])
                self.assertEqual(int(width), INTERACTION_THUMBNAIL_WIDTH_PX)
                self.assertEqual(int(height), INTERACTION_THUMBNAIL_HEIGHT_PX)

            interaction_ts_file_content = utils.get_file_contents(
                interaction_ts_file)
            response_ts_file_content = utils.get_file_contents(
                response_ts_file)
            short_response_ts_file_content = (
                utils.get_file_contents(short_response_ts_file))
            ts_file_content = utils.get_file_contents(ts_file)
            rules_service_ts_file_content = utils.get_file_contents(
                rules_service_ts_file)
            validation_service_ts_file_content = utils.get_file_contents(
                validation_service_ts_file)

            self.assertIn('oppiaInteractive%s' % interaction_id,
                          interaction_ts_file_content)
            self.assertIn('oppiaResponse%s' % interaction_id,
                          response_ts_file_content)
            self.assertIn('oppiaShortResponse%s' % interaction_id,
                          short_response_ts_file_content)
            self.assertIn(
                '%sRulesService' % (interaction_id[0] + interaction_id[1:]),
                rules_service_ts_file_content)
            self.assertIn('%sValidationService' % interaction_id,
                          validation_service_ts_file_content)

            # Check that the html template includes js script for the
            # interaction.
            self.assertTrue('oppia-interactive-%s.component.ts' %
                            hyphenated_interaction_id in ts_file_content
                            or ('oppia-interactive-%s.directive.ts' %
                                hyphenated_interaction_id in ts_file_content))
            self.assertTrue('oppia-response-%s.component.ts' %
                            hyphenated_interaction_id in ts_file_content
                            or ('oppia-response-%s.directive.ts' %
                                hyphenated_interaction_id in ts_file_content))
            self.assertTrue('oppia-short-response-%s.component.ts' %
                            hyphenated_interaction_id in ts_file_content
                            or ('oppia-short-response-%s.directive.ts' %
                                hyphenated_interaction_id in ts_file_content))
            self.assertIn('%s-rules.service.ts' % hyphenated_interaction_id,
                          ts_file_content)
            self.assertIn(
                '%s-validation.service.ts' % hyphenated_interaction_id,
                ts_file_content)

            self.assertNotIn('<script>', interaction_ts_file_content)
            self.assertNotIn('</script>', interaction_ts_file_content)
            self.assertNotIn('<script>', response_ts_file_content)
            self.assertNotIn('</script>', response_ts_file_content)
            self.assertNotIn('<script>', short_response_ts_file_content)
            self.assertNotIn('</script>', short_response_ts_file_content)
            self.assertNotIn('<script>', rules_service_ts_file_content)
            self.assertNotIn('</script>', rules_service_ts_file_content)
            self.assertNotIn('<script>', validation_service_ts_file_content)
            self.assertNotIn('</script>', validation_service_ts_file_content)

            interaction = interaction_registry.Registry.get_interaction_by_id(
                interaction_id)

            # Check that the specified interaction id is the same as the class
            # name.
            self.assertTrue(interaction_id, msg=interaction.__class__.__name__)

            # Check that the configuration file contains the correct
            # top-level keys, and that these keys have the correct types.
            for item, item_type in _INTERACTION_CONFIG_SCHEMA:
                self.assertTrue(
                    isinstance(getattr(interaction, item), item_type))
                if item_type == python_utils.BASESTRING:
                    self.assertTrue(getattr(interaction, item))

            self.assertIn(interaction.display_mode, base.ALLOWED_DISPLAY_MODES)

            if interaction.is_linear or interaction.is_terminal:
                self.assertIsNone(interaction.answer_type)
            else:
                # Check that the answer_type corresponds to a valid object
                # class.
                object_registry.Registry.get_object_class_by_type(
                    interaction.answer_type)

            self._validate_customization_arg_specs(
                interaction.customization_arg_specs)

            answer_visualization_specs = (
                interaction.answer_visualization_specs)
            self._validate_answer_visualization_specs(
                answer_visualization_specs)

            answer_visualizations = interaction.answer_visualizations
            for ind, visualization in enumerate(answer_visualizations):
                self.assertEqual(visualization.id,
                                 answer_visualization_specs[ind]['id'])
                self.assertEqual(
                    visualization.calculation_id,
                    answer_visualization_specs[ind]['calculation_id'])
                self.assertEqual(visualization.options,
                                 answer_visualization_specs[ind]['options'])

                # Check that the derived visualization is valid.
                visualization.validate()

            # Check that supplemental interactions have instructions, and
            # inline ones do not.
            if interaction.display_mode == base.DISPLAY_MODE_INLINE:
                self.assertIsNone(interaction.instructions)
                self.assertIsNone(interaction.narrow_instructions)
            else:
                self.assertTrue(
                    isinstance(interaction.instructions,
                               python_utils.BASESTRING))
                self.assertIsNotNone(interaction.instructions)
                self.assertIsNotNone(interaction.narrow_instructions)

            # Check that terminal interactions are not linear.
            if interaction.is_terminal:
                self.assertFalse(interaction.is_linear)

            # Check that only linear interactions have a
            # default_outcome_heading property.
            if interaction.is_linear:
                self.assertTrue(
                    isinstance(interaction.default_outcome_heading,
                               python_utils.BASESTRING)
                    and interaction.default_outcome_heading)
            else:
                self.assertIsNone(interaction.default_outcome_heading)

            # Check that interactions that can have solution cannot be linear.
            if interaction.can_have_solution:
                self.assertFalse(interaction.is_linear)

            default_object_values = object_registry.get_default_object_values()

            # Check that the rules for this interaction have object editor
            # templates and default values.
            for rule_name in list(interaction.rules_dict.keys()):
                param_list = interaction.get_rule_param_list(rule_name)

                for (_, param_obj_cls) in param_list:
                    # TODO(sll): Get rid of these special cases.
                    if param_obj_cls.__name__ in [
                            'NonnegativeInt', 'ListOfCodeEvaluation',
                            'ListOfCoordTwoDim', 'ListOfGraph',
                            'SetOfNormalizedString'
                    ]:
                        continue

                    # Check that the rule has a default value.
                    self.assertIn(param_obj_cls.__name__,
                                  default_object_values)