Merge: More package.ini
authorJean Privat <jean@pryen.org>
Mon, 12 Jun 2017 20:39:02 +0000 (16:39 -0400)
committerJean Privat <jean@pryen.org>
Mon, 12 Jun 2017 20:39:02 +0000 (16:39 -0400)
package.ini file are not mandatory, so to convince people to use then, here is a new tool.

Pull-Request: #2485
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Jean-Christophe Beaupré <jcbrinfo.public@gmail.com>

36 files changed:
contrib/action_nitro/src/action_nitro.nit
lib/app/http_request.nit
lib/dot/examples/clusters.nit [moved from lib/dot/exemples/clusters.nit with 99% similarity]
lib/dot/examples/hello.nit [moved from lib/dot/exemples/hello.nit with 100% similarity]
lib/dot/examples/undirected_clusters.nit [moved from lib/dot/exemples/undirected_clusters.nit with 100% similarity]
lib/gamnit/bmfont.nit
lib/gamnit/cameras.nit
lib/gamnit/cameras_cache.nit
lib/gamnit/depth/depth.nit
lib/gamnit/display.nit
lib/gamnit/display_linux.nit
lib/gamnit/dynamic_resolution.nit
lib/gamnit/flat.nit
lib/gamnit/gamnit.nit
lib/gamnit/gamnit_linux.nit
lib/geometry/points_and_lines.nit
lib/popcorn/pop_json.nit
lib/sdl2/events.nit
src/metrics/codesmells_metrics.nit [new file with mode: 0644]
src/metrics/mclassdef_collect.nit [new file with mode: 0644]
src/metrics/method_analyze_metrics.nit [new file with mode: 0644]
src/nitsmells.nit [new file with mode: 0644]
tests/TestNitsmells/FeatureEnvy/featureenvy.nit [new file with mode: 0644]
tests/TestNitsmells/LargeClass/largeclass.nit [new file with mode: 0644]
tests/TestNitsmells/LongMethod/longmethod.nit [new file with mode: 0644]
tests/TestNitsmells/LongParameterList/longparameterlist.nit [new file with mode: 0644]
tests/TestNitsmells/platform/platform.nit [new file with mode: 0644]
tests/nitsmells.args [new file with mode: 0644]
tests/sav/clusters.res [new file with mode: 0644]
tests/sav/hello.res [new file with mode: 0644]
tests/sav/nitsmells.res [new file with mode: 0644]
tests/sav/nitsmells_args1.res [new file with mode: 0644]
tests/sav/nitsmells_args2.res [new file with mode: 0644]
tests/sav/nitsmells_args3.res [new file with mode: 0644]
tests/sav/nitsmells_args4.res [new file with mode: 0644]
tests/sav/undirected_clusters.res [new file with mode: 0644]

index b8dd4f4..5a2c3bc 100644 (file)
@@ -562,7 +562,7 @@ redef class Player
                end
 
                # Display respawn instructions
-               app.ui_sprites.add new Sprite(app.texts_sheet.respawn, app.ui_camera.center)
+               app.ui_sprites.add new Sprite(app.texts_sheet.respawn, app.ui_camera.center.offset(0.0, 0.0, 0.0))
        end
 end
 
index 0260cab..8ab0f1d 100644 (file)
@@ -126,7 +126,7 @@ end
 #
 # Prints on communication errors and when the server returns an HTTP status code not in the 200s.
 #
-# ~~~
+# ~~~nitish
 # var request = new SimpleAsyncHttpRequest("http://example.com")
 # request.start
 # ~~~
similarity index 99%
rename from lib/dot/exemples/clusters.nit
rename to lib/dot/examples/clusters.nit
index 993d3ca..2361e0d 100644 (file)
@@ -66,7 +66,7 @@ graph.add_edge(b3, nend)
 
 if args.is_empty then
        print graph.to_dot
-       graph.show
+       # graph.show
 else
        graph.to_dot.write_to_file args.first
 end
index 4abdaa5..2cd69d7 100644 (file)
@@ -270,7 +270,8 @@ end
 # ~~~
 # redef class App
 #     var font = new BMFontAsset("arial.fnt")
-#     var ui_text = new TextSprites(font, ui_camera.top_left)
+#     var pos: Point3d[Float] = ui_camera.top_left.offset(128.0, -128.0, 0.0)
+#     var ui_text = new TextSprites(font, pos)
 #
 #     redef fun on_create
 #     do
index c9754f7..688082b 100644 (file)
@@ -29,7 +29,7 @@ abstract class Camera
        var display: GamnitDisplay
 
        # Position of this camera in world space
-       var position = new Point3d[Float](0.0, 0.0, 0.0) is writable
+       var position = new Point3d[Float](0.0, 0.0, 0.0)
 
        # The Model-View-Projection matrix created by this camera
        #
@@ -207,23 +207,21 @@ class UICamera
        # Clipping wall the farthest of the camera, defaults to -100.0
        var far: Float = -100.0 is writable
 
-       # Width in world units, defaults to the width in pixels of the screen
-       var width: Float = display.width.to_f is lazy
+       # Width in world units, calculated from `height` and the screen aspect ratio
+       fun width: Float do return height * display.aspect_ratio
 
-       # Height in world units, defaults to the height in pixels of the screen
-       var height: Float = display.height.to_f is lazy
+       # Height in world units, defaults to 1080.0
+       #
+       # Set this value using `reset_height`.
+       var height = 1080.0
 
        # Reset the camera position so that `height` world units are visible on the Y axis
        #
-       # By default, `height` is set to `display.height`.
-       #
        # This can be used to set standardized UI units independently from the screen resolution.
        fun reset_height(height: nullable Float)
        do
                if height == null then height = display.height.to_f
-
                self.height = height
-               self.width = height * display.aspect_ratio
        end
 
        # Convert the position `x, y` on screen, to UI coordinates
@@ -238,33 +236,31 @@ class UICamera
        end
 
        # Center of the screen, from the point of view of the camera, at z = 0
-       fun center: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y - height / 2.0, 0.0)
+       var center: IPoint3d[Float] = new CameraAnchor(self, 0.5, -0.5)
 
        # Center of the top of the screen, at z = 0
-       fun top: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y, 0.0)
+       var top: IPoint3d[Float] = new CameraAnchor(self, 0.5, 0.0)
 
        # Center of the bottom of the screen, at z = 0
-       fun bottom: Point3d[Float] do return new Point3d[Float](position.x + width / 2.0, position.y - height, 0.0)
+       var bottom: IPoint3d[Float] = new CameraAnchor(self, 0.5, -1.0)
 
        # Center of the left border of the screen, at z = 0
-       fun left: Point3d[Float] do return new Point3d[Float](position.x, position.y - height / 2.0, 0.0)
+       var left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -1.0)
 
        # Center of the right border of the screen, at z = 0
-       fun right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y - height / 2.0, 0.0)
+       var right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -1.0)
 
        # Top left corner of the screen, at z = 0
-       fun top_left: Point3d[Float] do return new Point3d[Float](position.x, position.y, 0.0)
+       var top_left: IPoint3d[Float] = new CameraAnchor(self, 0.0, 0.0)
 
        # Top right corner of the screen, at z = 0
-       fun top_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y, 0.0)
+       var top_right: IPoint3d[Float] = new CameraAnchor(self, 1.0, 0.0)
 
        # Bottom left corner of the screen, at z = 0
-       fun bottom_left: Point3d[Float] do return new Point3d[Float](position.x, position.y - height, 0.0)
+       var bottom_left: IPoint3d[Float] = new CameraAnchor(self, 0.0, -1.0)
 
        # Bottom right corner of the screen, at z = 0
-       fun bottom_right: Point3d[Float] do return new Point3d[Float](position.x + width, position.y - height, 0.0)
-
-       # TODO cache the anchors
+       var bottom_right: IPoint3d[Float] = new CameraAnchor(self, 1.0, -1.0)
 
        redef fun mvp_matrix
        do
@@ -279,3 +275,55 @@ class UICamera
                return view * projection
        end
 end
+
+# Immutable relative anchors for reference points on `camera`
+private class CameraAnchor
+       super IPoint3d[Float]
+
+       # Reference camera
+       var camera: UICamera
+
+       # Reference position, the top left of the screen
+       var ref: Point3d[Float] = camera.position is lazy
+
+       # X position as proportion of the screen width
+       var relative_x: Float
+
+       # Y position as proportion of the screen height
+       var relative_y: Float
+
+       redef fun x do return ref.x + relative_x*camera.width
+       redef fun y do return ref.y + relative_y*camera.height
+       redef fun z do return ref.z
+
+       redef fun offset(x, y, z) do return new OffsetPoint3d(self, x.to_f, y.to_f, z.to_f)
+end
+
+# Position relative to another point or usually a `CameraAnchor`
+private class OffsetPoint3d
+       super Point3d[Float]
+
+       autoinit ref, offset_x, offset_y, offset_z
+
+       # Reference point to which the offsets are applied
+       var ref: IPoint3d[Float]
+
+       # Difference on the X axis
+       var offset_x: Float
+
+       # Difference on the X axis
+       var offset_y: Float
+
+       # Difference on the X axis
+       var offset_z: Float
+
+       redef fun x do return ref.x + offset_x
+       redef fun y do return ref.y + offset_y
+       redef fun z do return ref.z + offset_z
+
+       redef fun x=(value) do if value != null then offset_x += value - x
+       redef fun y=(value) do if value != null then offset_y += value - y
+       redef fun z=(value) do if value != null then offset_z += value - z
+
+       redef fun offset(x, y, z) do return new OffsetPoint3d(self, x.to_f, y.to_f, z.to_f)
+end
index 08adc8f..852ac9b 100644 (file)
@@ -110,12 +110,6 @@ redef class UICamera
                mvp_matrix_cache = null
        end
 
-       redef fun width=(value)
-       do
-               super
-               mvp_matrix_cache = null
-       end
-
        redef fun height=(value)
        do
                super
index f18f2eb..9b4be47 100644 (file)
@@ -45,6 +45,7 @@ redef class App
        # Draw all elements of `actors` and then call `frame_core_flat`
        protected fun frame_core_depth(display: GamnitDisplay)
        do
+               glViewport(0, 0, display.width, display.height)
                frame_core_dynamic_resolution_before display
 
                # Update cameras on both our programs
index 6e7c58c..2b694c6 100644 (file)
@@ -34,7 +34,7 @@ class GamnitDisplay
        fun height: Int is abstract
 
        # Aspect ratio of the screen, `width / height`
-       var aspect_ratio: Float is lazy do return width.to_f / height.to_f
+       fun aspect_ratio: Float do return width.to_f / height.to_f
 
        # Is the cursor locked et the center of the screen?
        var lock_cursor = false is writable
index cb6bdf0..e3ab243 100644 (file)
@@ -82,7 +82,7 @@ redef class GamnitDisplay
                        print_error "Failed to initialize SDL2 IMG: {sdl.error}"
                end
 
-               var sdl_window = new SDLWindow(window_title.to_cstring, window_width, window_height, (new SDLWindowFlags).opengl)
+               var sdl_window = new SDLWindow(window_title.to_cstring, window_width, window_height, sdl_window_flags)
                assert not sdl_window.address_is_null else
                        print_error "Failed to create SDL2 window: {sdl.error}"
                end
@@ -101,7 +101,12 @@ redef class GamnitDisplay
                return sdl_window
        end
 
-       # Initialization flags passed to SDL2 mixer
+       # SDL2 window initialization flags
+       #
+       # The default value supports OpenGL and window resizing.
+       var sdl_window_flags: SDLWindowFlags = (new SDLWindowFlags).opengl.resizable is lazy, writable
+
+       # SDL2 mixer initialization flags
        #
        # Defaults to all available formats.
        var mix_init_flags: MixInitFlags = mix.flac | mix.mod | mix.mp3 | mix.ogg is lazy, writable
index 40ea221..97efba6 100644 (file)
@@ -64,6 +64,12 @@ redef class App
                assert error == null else print_error error
        end
 
+       redef fun on_resize(display)
+       do
+               dynamic_context.resize(display, max_dynamic_resolution_ratio)
+               super
+       end
+
        # Prepare to draw to the dynamic screen if `dynamic_resolution_ratio != 1.0`
        protected fun frame_core_dynamic_resolution_before(display: GamnitDisplay)
        do
@@ -73,6 +79,7 @@ redef class App
                        # Draw directly to the screen framebuffer
                        glBindFramebuffer(gl_FRAMEBUFFER, dynamic_context.screen_framebuffer)
                        glViewport(0, 0, display.width, display.height)
+                       glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
 
                        var gl_error = glGetError
                        assert gl_error == gl_NO_ERROR else print_error gl_error
@@ -81,13 +88,14 @@ redef class App
 
                # Draw to our dynamic framebuffer
                glBindFramebuffer(gl_FRAMEBUFFER, dynamic_context.dynamic_framebuffer)
-               glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
 
                var ratio = dynamic_resolution_ratio
                ratio = ratio.clamp(min_dynamic_resolution_ratio, max_dynamic_resolution_ratio)
                glViewport(0, 0, (display.width.to_f*ratio).to_i,
                                                 (display.height.to_f*ratio).to_i)
 
+               glClear gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT
+
                var gl_error = glGetError
                assert gl_error == gl_NO_ERROR else print_error gl_error
        end
@@ -176,9 +184,6 @@ private class DynamicContext
        do
                # TODO enable antialiasing.
 
-               var width = (display.width.to_f * max_dynamic_resolution_ratio).to_i
-               var height = (display.height.to_f * max_dynamic_resolution_ratio).to_i
-
                # Set aside the real screen framebuffer name
                var screen_framebuffer = glGetIntegerv(gl_FRAMEBUFFER_BINDING, 0)
                self.screen_framebuffer = screen_framebuffer
@@ -188,34 +193,19 @@ private class DynamicContext
                glBindFramebuffer(gl_FRAMEBUFFER, framebuffer)
                assert glIsFramebuffer(framebuffer)
                self.dynamic_framebuffer = framebuffer
+               var gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
 
-               # Depth
+               # Depth & texture/color
                var depthbuffer = glGenRenderbuffers(1).first
-               glBindRenderbuffer(gl_RENDERBUFFER, depthbuffer)
-               assert glIsRenderbuffer(depthbuffer)
-               glRenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_COMPNENT16, width, height)
-               glFramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_RENDERBUFFER, depthbuffer)
                self.depth_renderbuffer = depthbuffer
-
-               # Texture
                var texture = glGenTextures(1).first
-               glBindTexture(gl_TEXTURE_2D, texture)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
-               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
-               glTexImage2D(gl_TEXTURE_2D, 0, gl_RGB, width, height,
-                            0, gl_RGB, gl_UNSIGNED_BYTE, new Pointer.nul)
-               glFramebufferTexture2D(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, gl_TEXTURE_2D, texture, 0)
                self.texture = texture
-
-               var gl_error = glGetError
+               gl_error = glGetError
                assert gl_error == gl_NO_ERROR else print_error gl_error
-               assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
 
-               # Take down
-               glBindRenderbuffer(gl_RENDERBUFFER, 0)
-               glBindFramebuffer(gl_FRAMEBUFFER, 0)
+               resize(display, max_dynamic_resolution_ratio)
+               assert glCheckFramebufferStatus(gl_FRAMEBUFFER) == gl_FRAMEBUFFER_COMPLETE
 
                # Array buffer
                buffer_array = glGenBuffers(1).first
@@ -244,6 +234,46 @@ private class DynamicContext
                assert gl_error == gl_NO_ERROR else print_error gl_error
        end
 
+       # Init size or resize `depth_renderbuffer` and `texture`
+       fun resize(display: GamnitDisplay, max_dynamic_resolution_ratio: Float)
+       do
+               var width = (display.width.to_f * max_dynamic_resolution_ratio).to_i
+               var height = (display.height.to_f * max_dynamic_resolution_ratio).to_i
+
+               glBindFramebuffer(gl_FRAMEBUFFER, dynamic_framebuffer)
+
+               var depthbuffer = self.depth_renderbuffer
+               var texture = self.texture
+
+               # Depth
+               glBindRenderbuffer(gl_RENDERBUFFER, depthbuffer)
+               assert glIsRenderbuffer(depthbuffer)
+               glRenderbufferStorage(gl_RENDERBUFFER, gl_DEPTH_COMPNENT16, width, height)
+               glFramebufferRenderbuffer(gl_FRAMEBUFFER, gl_DEPTH_ATTACHMENT, gl_RENDERBUFFER, depthbuffer)
+               var gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+
+               # Texture
+               glBindTexture(gl_TEXTURE_2D, texture)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
+               glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
+               glTexImage2D(gl_TEXTURE_2D, 0, gl_RGB, width, height,
+                            0, gl_RGB, gl_UNSIGNED_BYTE, new Pointer.nul)
+               glFramebufferTexture2D(gl_FRAMEBUFFER, gl_COLOR_ATTACHMENT0, gl_TEXTURE_2D, texture, 0)
+
+               gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+
+               # Take down
+               glBindRenderbuffer(gl_RENDERBUFFER, 0)
+               glBindFramebuffer(gl_FRAMEBUFFER, 0)
+
+               gl_error = glGetError
+               assert gl_error == gl_NO_ERROR else print_error gl_error
+       end
+
        var destroyed = false
        fun destroy
        do
index cbcc2e7..b278029 100644 (file)
@@ -41,7 +41,8 @@ import more_collections
 import performance_analysis
 
 import gamnit
-import gamnit::cameras_cache
+intrude import gamnit::cameras
+intrude import gamnit::cameras_cache
 import gamnit::dynamic_resolution
 import gamnit::limit_fps
 import gamnit::camera_control
@@ -293,9 +294,7 @@ redef class App
        do
                texture.load
 
-               ui_camera.reset_height 1080.0
-
-               var splash = new Sprite(texture, ui_camera.center)
+               var splash = new Sprite(texture, ui_camera.center.offset(0.0, 0.0, 0.0))
                ui_sprites.add splash
 
                var display = display
@@ -373,11 +372,19 @@ redef class App
                if display != null then display.close
        end
 
-       redef fun frame_core(display)
+       redef fun on_resize(display)
        do
-               # Prepare to draw, clear buffers
-               glClear(gl_COLOR_BUFFER_BIT | gl_DEPTH_BUFFER_BIT)
+               super
+
+               world_camera.mvp_matrix_cache = null
+               ui_camera.mvp_matrix_cache = null
 
+               # Update all sprites in the UI
+               for sprite in ui_sprites do sprite.needs_update
+       end
+
+       redef fun frame_core(display)
+       do
                # Check errors
                var gl_error = glGetError
                assert gl_error == gl_NO_ERROR else print_error gl_error
@@ -585,12 +592,6 @@ private class Simple2dProgram
 end
 
 redef class Point3d[N]
-       # Get a new `Point3d[Float]` with an offset of each axis of `x, y, z`
-       fun offset(x, y, z: Numeric): Point3d[Float]
-       do
-               return new Point3d[Float](self.x.to_f+x.to_f, self.y.to_f+y.to_f, self.z.to_f+z.to_f)
-       end
-
        # ---
        # Associate each point to its sprites
 
@@ -641,6 +642,26 @@ redef class Point3d[N]
        end
 end
 
+redef class OffsetPoint3d
+       redef fun x=(v)
+       do
+               if isset _x and v != x then needs_update
+               super
+       end
+
+       redef fun y=(v)
+       do
+               if isset _y and v != y then needs_update
+               super
+       end
+
+       redef fun z=(v)
+       do
+               if isset _z and v != z then needs_update
+               super
+       end
+end
+
 # Set of sprites sorting them into different `SpriteContext`
 private class SpriteSet
        super HashSet[Sprite]
index 0d39f1e..fccf2ae 100644 (file)
@@ -77,4 +77,9 @@ redef class App
        # The instances passed as `event` may be freed (or overwritten),
        # right after this method returns. They should not be preserved.
        fun accept_event(event: InputEvent): Bool do return false
+
+       # The window has been resized by the user or system
+       #
+       # The framework handles resizing the viewport automatically.
+       fun on_resize(display: GamnitDisplay) do end
 end
index e68ee8e..b0c4241 100644 (file)
@@ -18,6 +18,7 @@ module gamnit_linux
 import sdl2::events
 
 intrude import gamnit
+intrude import display_linux
 
 redef class App
        private var sdl_event_buffer = new SDLEventBuffer.malloc
@@ -33,6 +34,12 @@ redef class App
 
                        # Convert to an SDL event with data
                        var sdl_event = sdl_event_buffer.to_event
+                       if sdl_event isa SDLWindowEvent and sdl_event.is_resized then
+                               display.width = sdl_event.data1
+                               display.height = sdl_event.data2
+                               on_resize display
+                       end
+
                        # Convert to `mnit::inputs` conforming objects
                        var gamnit_event = sdl_event.to_gamnit_event(sdl_event_buffer)
                        accept_event gamnit_event
index 16efae0..22d2c51 100644 (file)
@@ -142,6 +142,17 @@ interface IPoint3d[N: Numeric]
                var s = dist2_xy(other).add(dz.mul(dz))
                return x.value_of(s)
        end
+
+       # Get a new `Point3d[Float]` at an offset of `x, y, z` from `self`
+       #
+       # ~~~
+       # var origin = new Point3d[Float](1.0, 1.0, 1.0)
+       # assert origin.offset(1.0, 2.0, 3.0).to_s == "(2.0, 3.0, 4.0)"
+       # ~~~
+       fun offset(x, y, z: Numeric): Point3d[Float]
+       do return new Point3d[Float](self.x.to_f+x.to_f,
+                                    self.y.to_f+y.to_f,
+                                    self.z.to_f+z.to_f)
 end
 
 # 3D point with `x`, `y` and `z`
index 64a06e1..8b14fc8 100644 (file)
@@ -56,6 +56,7 @@
 module pop_json
 
 import json
+import meta
 import pop_handlers
 import pop_validation
 
@@ -147,7 +148,7 @@ redef class Handler
        fun deserialize_body(req: HttpRequest, res: HttpResponse): nullable BODY do
                var body = req.body
                var deserializer = new JsonDeserializer(body)
-               var form = deserializer.deserialize(body)
+               var form = deserializer.deserialize(body_type)
                if not form isa BODY or deserializer.errors.not_empty then
                        res.json_error("Bad input", 400)
                        return null
@@ -159,6 +160,8 @@ redef class Handler
        #
        # Define it in each sub handlers depending on the kind of objects sent in request bodies.
        type BODY: Serializable
+
+       private var body_type: String is lazy do return (new GetName[BODY]).to_s
 end
 
 redef class HttpResponse
index 6506dfd..2ca18aa 100644 (file)
@@ -50,6 +50,7 @@ extern class SDLEventBuffer `{SDL_Event *`}
                if is_mouse_wheel then return to_mouse_wheel
                if is_keydown then return to_keydown
                if is_keyup then return to_keyup
+               if is_window then return to_window
                return to_event_direct
        end
 
@@ -111,6 +112,14 @@ extern class SDLEventBuffer `{SDL_Event *`}
        # Require: `is_keyup`
        fun to_keyup: SDLKeyboardUpEvent `{ return self; `}
 
+       #  Is this a window event?
+       fun is_window: Bool `{ return self->type == SDL_WINDOWEVENT; `}
+
+       # Get a reference to data at `self` as a `SDLWindowEvent`
+       #
+       # Require: `is_window`
+       fun to_window: SDLWindowEvent `{ return self; `}
+
        # TODO other SDL events:
        #
        # SDL_CommonEvent common
@@ -277,3 +286,23 @@ extern class SDLKeysym `{ SDL_Keysym * `}
        fun mod: Int `{ return self->mod; `}
        # TODO related masks
 end
+
+# Window event
+extern class SDLWindowEvent
+       super SDLEvent
+
+       # Window identifier
+       fun id: Int `{ return self->window.windowID; `}
+
+       # Is `self` a resized event?
+       fun is_resized: Bool `{ return self->window.event == SDL_WINDOWEVENT_RESIZED; `}
+
+       # Is `self` a size changed event?
+       fun is_size_changed: Bool `{ return self->window.event == SDL_WINDOWEVENT_SIZE_CHANGED; `}
+
+       # `data1` field, depends on the event kind
+       fun data1: Int `{ return self->window.data1; `}
+
+       # `data2` field, depends on the event kind
+       fun data2: Int `{ return self->window.data2; `}
+end
diff --git a/src/metrics/codesmells_metrics.nit b/src/metrics/codesmells_metrics.nit
new file mode 100644 (file)
index 0000000..1bd502d
--- /dev/null
@@ -0,0 +1,317 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Detect the code smells and antipatterns in the code.
+
+module codesmells_metrics
+
+import frontend
+import metrics_base
+import mclasses_metrics
+import semantize
+import method_analyze_metrics
+import mclassdef_collect
+
+redef class ToolContext
+       var codesmells_metrics_phase = new CodeSmellsMetricsPhase(self, null)
+end
+
+class CodeSmellsMetricsPhase
+       super Phase
+       var average_number_of_lines = 0.0
+       var average_number_of_parameter = 0.0
+       var average_number_of_method = 0.0
+       var average_number_of_attribute = 0.0
+
+       redef fun process_mainmodule(mainmodule, given_mmodules) do
+               print toolcontext.format_h1("--- Code Smells Metrics ---")
+               self.set_all_average_metrics
+               var mclass_codesmell = new BadConceptonController
+               var collect = new Counter[MClassDef]
+               var mclassdefs = new Array[MClassDef]
+
+               for mclass in mainmodule.flatten_mclass_hierarchy do
+                       mclass_codesmell.collect(mclass.mclassdefs,self)
+               end
+               mclass_codesmell.print_top(10)
+       end
+
+       fun set_all_average_metrics do
+               var model_builder = toolcontext.modelbuilder
+               var model_view = model_builder.model.private_view
+               self.average_number_of_lines = model_view.get_avg_linenumber(model_builder)
+               self.average_number_of_parameter = model_view.get_avg_parameter
+               self.average_number_of_method = model_view.get_avg_method
+               self.average_number_of_attribute = model_view.get_avg_attribut
+       end
+end
+
+class BadConceptonController
+       # Code smell list
+       var bad_conception_elements = new Array[BadConceptionFinder]
+
+       # Print all element conception
+       fun print_all do
+               for bad_conception in bad_conception_elements do
+                       bad_conception.print_all
+               end
+       end
+
+       # Print number of top element conception
+       fun print_top(number: Int) do
+               var test = self.get_numbers_of_elements(number)
+               for bad_conception in test do
+                       bad_conception.print_all
+               end
+       end
+
+       # Collection
+       fun collect(mclassdefs: Array[MClassDef],phase: CodeSmellsMetricsPhase) do
+               for mclassdef in mclassdefs do
+                       var bad_conception_class = new BadConceptionFinder(mclassdef,phase)
+                       bad_conception_class.collect
+                       bad_conception_elements.add(bad_conception_class)
+               end
+       end
+
+       fun sort: Array[BadConceptionFinder]
+       do
+               var res = bad_conception_elements
+               var sorter = new BadConceptionComparator
+               sorter.sort(res)
+               return res
+       end
+
+       fun get_numbers_of_elements(number : Int) : Array[BadConceptionFinder]do
+               var return_values = new Array[BadConceptionFinder]
+               var list = self.sort
+               var min = number
+               if list.length <= number*2 then min = list.length
+               for i in [0..min[ do
+                       var t = list[list.length-i-1]
+                       return_values.add(t)
+               end
+               return return_values
+       end
+end
+
+class BadConceptionFinder
+       var mclassdef: MClassDef
+       var array_badconception = new Array[BadConception]
+       var phase: CodeSmellsMetricsPhase
+
+       fun collect do
+               var bad_conception_elements = new Array[BadConception]
+               bad_conception_elements.add(new LargeClass(phase))
+               bad_conception_elements.add(new LongParameterList(phase))
+               bad_conception_elements.add(new FeatureEnvy(phase))
+               bad_conception_elements.add(new LongMethod(phase))
+               for bad_conception_element in bad_conception_elements do
+                       if bad_conception_element.collect(self.mclassdef,phase.toolcontext.modelbuilder) == true then array_badconception.add(bad_conception_element)
+               end
+       end
+
+       fun print_all do
+               if array_badconception.length != 0 then
+                       print "-----------"
+                       print "{mclassdef.full_name}"
+                       for bad_conception in array_badconception do
+                               bad_conception.print_result
+                       end
+               end
+       end
+end
+
+class BadConception
+       var phase: CodeSmellsMetricsPhase
+
+       # Name
+       fun name: String is abstract
+
+       # Description
+       fun desc: String is abstract
+
+       # Collection method
+       fun collect(mclassdef: MClassDef, model_builder: ModelBuilder): Bool is abstract
+
+       # Show results in console
+       fun print_result is abstract
+end
+
+class LargeClass
+       super BadConception
+       var number_attribut = 0
+
+       var number_method = 0
+
+       redef fun name do return "LARGC"
+
+       redef fun desc do return "Large class"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               number_attribut = mclassdef.collect_intro_and_redef_mattributes(model_builder.model.private_view).length
+               # get the number of methods (subtract the get and set of attibutes with (numberAtribut*2))
+               number_method = mclassdef.collect_intro_and_redef_methods(model_builder.model.private_view).length
+               return number_method.to_f > phase.average_number_of_method and number_attribut.to_f > phase.average_number_of_attribute
+       end
+
+       redef fun print_result do
+               print phase.toolcontext.format_h2("{desc}: {number_attribut} attributes and {number_method} methods ({phase.average_number_of_attribute}A {phase.average_number_of_method}M Average)")
+       end
+end
+
+class LongParameterList
+       super BadConception
+       var bad_methods = new Array[MMethodDef]
+
+       redef fun name do return "LONGPL"
+
+       redef fun desc do return "Long parameter list"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               for meth in mclassdef.collect_intro_and_redef_mpropdefs(model_builder.model.private_view) do
+                       # check if the property is a method definition
+                       if not meth isa MMethodDef then continue
+                       # Check if method has a signature
+                       if meth.msignature == null then continue
+                       if meth.msignature.mparameters.length <= 4 then continue
+                       bad_methods.add(meth)
+               end
+               return bad_methods.not_empty
+       end
+
+
+       redef fun print_result do
+               print "{desc}:"
+               if bad_methods.length >= 1 then
+                       print " Affected method(s):"
+                       for method in bad_methods do
+                               print "         -{method.name} has {method.msignature.mparameters.length} parameters"
+                       end
+               end
+       end
+end
+
+class FeatureEnvy
+       super BadConception
+       var bad_methods = new Array[MMethodDef]
+
+       redef fun name do return "FEM"
+
+       redef fun desc do return "Feature envy"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               var mmethoddefs = call_analyze_methods(mclassdef,model_builder)
+               for mmethoddef in mmethoddefs do
+                       if mmethoddef.total_extern_call <= mmethoddef.total_self_call then continue
+                       bad_methods.add(mmethoddef)
+               end
+               return bad_methods.not_empty
+       end
+
+       redef fun print_result do
+               print "{desc}:"
+               if bad_methods.length >= 1 then
+                       print " Affected method(s):"
+                       for method in bad_methods do
+                               print "         -{method.name} {method.total_self_call}/{method.total_call}"
+                       end
+               end
+       end
+end
+
+class LongMethod
+       super BadConception
+       var bad_methods = new Array[MMethodDef]
+
+       redef fun name do return "LONGMETH"
+
+       redef fun desc do return "Long method"
+
+       redef fun collect(mclassdef, model_builder): Bool do
+               var mmethoddefs = call_analyze_methods(mclassdef,model_builder)
+               for mmethoddef in mmethoddefs do
+                       if mmethoddef.line_number <= phase.average_number_of_lines.to_i then continue
+                       bad_methods.add(mmethoddef)
+               end
+               return bad_methods.not_empty
+       end
+
+       redef fun print_result do
+               print "{desc}:  Average {phase.average_number_of_lines.to_i} lines"
+               if bad_methods.length >= 1 then
+                       print " Affected method(s):"
+                       for method in bad_methods do
+                               print "         -{method.name} has {method.line_number} lines"
+                       end
+               end
+       end
+end
+
+redef class ModelView
+       fun get_avg_parameter: Float do
+               var counter = new Counter[MMethodDef]
+               for mclassdef in mclassdefs do
+                       for method in mclassdef.collect_intro_and_redef_mpropdefs(self) do
+                       # check if the property is a method definition
+                               if not method isa MMethodDef then continue
+                               #Check if method has a signature
+                               if method.msignature == null then continue
+                               if method.msignature.mparameters.length == 0 then continue
+                               counter[method] = method.msignature.mparameters.length
+                       end
+               end
+               return counter.avg + counter.std_dev
+       end
+
+       fun get_avg_attribut: Float do
+               var counter = new Counter[MClassDef]
+               for mclassdef in mclassdefs do
+                       var number_attributs = mclassdef.collect_intro_and_redef_mattributes(self).length
+                       if number_attributs != 0 then counter[mclassdef] = number_attributs
+               end
+               return counter.avg + counter.std_dev
+       end
+
+       fun get_avg_method: Float do
+               var counter = new Counter[MClassDef]
+               for mclassdef in mclassdefs do
+                       var number_methodes = mclassdef.collect_intro_and_redef_methods(self).length
+                       if number_methodes != 0 then counter[mclassdef] = number_methodes
+               end
+               return counter.avg + counter.std_dev
+       end
+
+       fun get_avg_linenumber(model_builder: ModelBuilder): Float do
+               var methods_analyse_metrics = new Counter[MClassDef]
+               for mclassdef in mclassdefs do
+                       var result = 0
+                       var count = 0
+                       for mmethoddef in call_analyze_methods(mclassdef,model_builder) do
+                               result += mmethoddef.line_number
+                               if mmethoddef.line_number == 0 then continue
+                               count += 1
+                       end
+                       if not mclassdef.collect_local_mproperties(self).length != 0 then continue
+                       if count == 0 then continue
+                       methods_analyse_metrics[mclassdef] = (result/count).to_i
+               end
+               return methods_analyse_metrics.avg + methods_analyse_metrics.std_dev
+       end
+end
+
+class BadConceptionComparator
+       super Comparator
+       redef type COMPARED: BadConceptionFinder
+       redef fun compare(a,b) do return a.array_badconception.length <=> b.array_badconception.length
+end
diff --git a/src/metrics/mclassdef_collect.nit b/src/metrics/mclassdef_collect.nit
new file mode 100644 (file)
index 0000000..5b6c937
--- /dev/null
@@ -0,0 +1,167 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# This module redef Mclassdef to add new collect methods.
+
+module mclassdef_collect
+
+# We usualy need specific phases
+# NOTE: `frontend` is sufficent in most case (it is often too much)
+import frontend
+import model_views
+import model_collect
+
+redef class MClassDef
+       # Collect all mproperties introduced in 'self' with `visibility >= min_visibility`.
+       fun collect_intro_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+                       for mprop in self.intro_mproperties do
+                               if not view.accept_mentity(mprop) then continue
+                               set.add(mprop)
+                       end
+               return set
+       end
+
+       # Collect mmethods introduced in 'self' with `visibility >= min_visibility`.
+       fun collect_intro_mmethods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_intro_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MMethod then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mmethods redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_redef_mmethods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_redef_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MMethod then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mattributes redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_redef_mattributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_redef_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mattributes introduced in 'self' with `visibility >= min_visibility`.
+       fun collect_intro_mattributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_intro_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect all mproperties redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_redef_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               for mpropdef in self.mpropdefs do
+                       if not view.accept_mentity(mpropdef) then continue
+                       if mpropdef.mproperty.intro_mclassdef.mclass == self then continue
+                               set.add(mpropdef.mproperty)
+                       end
+               return set
+       end
+
+       # Collect mmethods inherited by 'self' if accepted by `view`.
+       fun collect_inherited_mmethods(view: ModelView): Set[MMethod] do
+               var res = new HashSet[MMethod]
+               for mproperty in collect_inherited_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MMethod then res.add(mproperty)
+               end
+               return res
+       end
+
+       # Collect mproperties introduced and redefined in 'self' with `visibility >= min_visibility`.
+       fun collect_local_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               set.add_all collect_intro_mproperties(view)
+               set.add_all collect_redef_mproperties(view)
+               return set
+       end
+
+       # Collect all mproperties inehrited by 'self' with `visibility >= min_visibility`.
+       fun collect_inherited_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               for parent in collect_parents(view) do
+                       set.add_all(parent.collect_intro_mproperties(view))
+                       set.add_all(parent.collect_inherited_mproperties(view))
+               end
+               return set
+       end
+
+       # Collect mattributes inherited by 'self' with `visibility >= min_visibility`.
+       fun collect_inherited_mattributes(view: ModelView): Set[MAttribute] do
+               var res = new HashSet[MAttribute]
+               for mproperty in collect_inherited_mproperties(view) do
+                       if not view.accept_mentity(mproperty) then continue
+                       if mproperty isa MAttribute then res.add(mproperty)
+               end
+               return res
+       end
+
+       fun collect_all_methods(view: ModelView): Set[MMethod] do
+               var set = new HashSet[MMethod]
+               set.add_all collect_intro_mmethods(view)
+               set.add_all collect_redef_mmethods(view)
+               set.add_all collect_inherited_mmethods(view)
+               return set
+       end
+
+       fun collect_all_mattributes(view: ModelView): Set[MAttribute] do
+               var set = new HashSet[MAttribute]
+               set.add_all collect_redef_mattributes(view)
+               set.add_all collect_intro_mattributes(view)
+               set.add_all collect_inherited_mattributes(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_methods(view: ModelView): Set[MMethod] do
+               var set = new HashSet[MMethod]
+               set.add_all collect_intro_mmethods(view)
+               set.add_all collect_redef_mmethods(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_mattributes(view: ModelView): Set[MAttribute] do
+               var set = new HashSet[MAttribute]
+               set.add_all collect_redef_mattributes(view)
+               set.add_all collect_intro_mattributes(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_mproperties(view: ModelView): Set[MProperty] do
+               var set = new HashSet[MProperty]
+               set.add_all collect_redef_mproperties(view)
+               set.add_all collect_intro_mproperties(view)
+               return set
+       end
+
+       fun collect_intro_and_redef_mpropdefs(view: ModelView): Set[MPropDef] do
+               var set = new HashSet[MPropDef]
+               set.add_all collect_intro_mpropdefs(view)
+               set.add_all collect_redef_mpropdefs(view)
+               return set
+       end
+end
diff --git a/src/metrics/method_analyze_metrics.nit b/src/metrics/method_analyze_metrics.nit
new file mode 100644 (file)
index 0000000..6aee2a9
--- /dev/null
@@ -0,0 +1,83 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# This module call visitor for get number of line, total attributs call and total of self attributes call
+
+module method_analyze_metrics
+
+# We usualy need specific phases
+# NOTE: `frontend` is sufficent in most case (it is often too much)
+import metrics_base
+import mclasses_metrics
+import semantize
+import mclassdef_collect
+
+
+fun call_analyze_methods(mclassdef: MClassDef, model_builder: ModelBuilder): Array[MMethodDef] do
+       var mmethoddefs = new Array[MMethodDef]
+       for m_prop in mclassdef.collect_intro_and_redef_mpropdefs(model_builder.model.private_view) do
+               var n_prop = model_builder.mpropdef2node(m_prop)
+               #Check if the property is a method definition
+               if n_prop isa AMethPropdef and m_prop isa MMethodDef then
+                       if n_prop.n_methid isa AIdMethid then
+                               #Call visitor to analyse the method
+                               var visitor = new MethodAnalyzeMetrics(n_prop)
+                               visitor.enter_visit(n_prop)
+                               mmethoddefs.add(set_analyse_result_methoddef(m_prop,visitor))
+                       end
+               end
+       end
+       return mmethoddefs
+end
+
+fun set_analyse_result_methoddef(mmethoddef: MMethodDef, visitor: MethodAnalyzeMetrics): MMethodDef do
+       mmethoddef.total_call = visitor.total_call
+       mmethoddef.line_number = visitor.line_number.length
+       mmethoddef.total_self_call = visitor.total_self_call
+       mmethoddef.total_extern_call = visitor.total_call - visitor.total_self_call
+       return mmethoddef
+end
+
+public class MethodAnalyzeMetrics
+       super Visitor
+       var ameth_prop_def: AMethPropdef
+       var total_call = 0
+       var line_number = new Counter[nullable Int]
+       var total_self_call = 0
+
+       redef fun visit(n) do
+               n.visit_all(self)
+               if n isa AExpr then
+                       if not n isa ABlockExpr then
+                               if n.first_location != null then
+                                       line_number.inc(n.first_location.line_start)
+                               end
+                       end
+               end
+
+               if n isa ASendExpr then
+                       var callsite = n.callsite
+                       if callsite != null then
+                               self.total_call += 1
+                               if callsite.recv_is_self == true then self.total_self_call += 1
+                       end
+               end
+       end
+end
+
+redef class MMethodDef
+       var total_call = 0
+       var line_number = 0
+       var total_self_call = 0
+       var total_extern_call = 0
+end
diff --git a/src/nitsmells.nit b/src/nitsmells.nit
new file mode 100644 (file)
index 0000000..da8454a
--- /dev/null
@@ -0,0 +1,36 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import metrics_base
+import mclasses_metrics
+import semantize
+
+import codesmells_metrics
+
+# Create a tool context to handle options and paths
+var toolcontext = new ToolContext
+toolcontext.tooldescription = "Usage: nitsmells [OPTION]... <file.nit>...\n Computes code smells on Nit programs."
+# We do not add other options, so process them now!
+toolcontext.process_options(args)
+# Get arguments
+var arguments = toolcontext.option_context.rest
+# We need a model to collect stufs
+var model = new Model
+# An a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext)
+# Here we load an process all modules passed on the command line
+var mmodules = modelbuilder.parse_full(arguments)
+modelbuilder.run_phases
+print "*** CODE SMELLS METRICS ***"
+toolcontext.run_global_phases(mmodules)
diff --git a/tests/TestNitsmells/FeatureEnvy/featureenvy.nit b/tests/TestNitsmells/FeatureEnvy/featureenvy.nit
new file mode 100644 (file)
index 0000000..6a11274
--- /dev/null
@@ -0,0 +1,42 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A test program with a fake model to check model tools.
+module featureenvy
+
+import platform
+
+class Starter
+       var attribute = 0
+       var attribute1 = 0
+       var attribute2 = 0
+       fun start do
+               self.attribute1 = 10
+               self.attribute = 2
+       end
+
+       fun ended do end
+end
+
+class FeatureEnvy
+       var testVariable = 0
+
+       fun feature_envy_method do
+               var starter = new Starter
+               testVariable = starter.attribute
+               if starter.attribute == self.testVariable then
+                       starter.attribute = 10
+               end
+       end
+end
diff --git a/tests/TestNitsmells/LargeClass/largeclass.nit b/tests/TestNitsmells/LargeClass/largeclass.nit
new file mode 100644 (file)
index 0000000..1c18141
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A test program with a fake model to check model tools.
+module largeclass
+
+import platform
+
+class LargeClass
+       var attribute = 0
+       var attribute1 = 0
+       var attribute2 = 0
+       var attribute3 = 0
+       var attribute4 = 0
+       var attribute5 = 0
+       var attribut6 = 0
+       var attribute7 = 0
+       var attribute8 = 0
+       var attribute9 = 0
+       var attribute10 = 0
+       var attribute11 = 0
+       var attribute12 = 0
+       var attribute13 = 0
+       var attribute14 = 0
+       var attribute15 = 0
+       var attribute16 = 0
+       var attribute17 = 0
+
+       fun start do
+               self.attribute16 = 10
+       end
+
+       fun ended do end
+       fun replay do end
+       fun restart do end
+       fun start1 do end
+       fun ended1 do end
+       fun replay1 do end
+       fun restart1 do end
+       fun start2 do end
+       fun ended2 do end
+       fun replay2 do end
+       fun restart2 do end
+end
+
+class NoLargeclass
+       var testVariable = 0
+
+       fun test do end
+end
\ No newline at end of file
diff --git a/tests/TestNitsmells/LongMethod/longmethod.nit b/tests/TestNitsmells/LongMethod/longmethod.nit
new file mode 100644 (file)
index 0000000..584ef44
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A test program with a fake model to check model tools.
+module longmethod
+
+import platform
+
+class Starter
+       var attribute = 0
+       var attribute1 = 0
+       var attribute2 = 0
+       fun start do
+               self.attribute1 = 10
+       end
+
+       fun ended do end
+end
+
+class LongMethodClass
+       var test_variable = 0
+
+       fun long_method do
+               var starter = new Starter
+               test_variable = 3
+       end
+end
\ No newline at end of file
diff --git a/tests/TestNitsmells/LongParameterList/longparameterlist.nit b/tests/TestNitsmells/LongParameterList/longparameterlist.nit
new file mode 100644 (file)
index 0000000..6988536
--- /dev/null
@@ -0,0 +1,34 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A test program with a fake model to check model tools.
+module longparameterlist
+
+import platform
+
+class Starter
+       fun no_para do end
+
+       fun no_para2() do end
+end
+
+class Test
+       fun short_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int) do
+               var starter = new Starter
+       end
+
+       fun long_list_parameter(numbers : Int, para1 : Bool, para2 : Float, para3 : Int, para4 : Starter) do
+               var starter = new Starter
+       end
+end
diff --git a/tests/TestNitsmells/platform/platform.nit b/tests/TestNitsmells/platform/platform.nit
new file mode 100644 (file)
index 0000000..175e6fc
--- /dev/null
@@ -0,0 +1,59 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Declares base types allowed on the platform.
+module platform
+
+import end
+
+# Root of everything.
+interface Object
+       # Used for comparisons.
+       type OTHER: nullable Object
+
+       # Is `other` equqls to `self`?
+       fun ==(other: OTHER): Bool is intern
+
+       # Is `other` different from `self`?
+       fun !=(other: OTHER): Bool do return not self == other
+end
+
+# Some services about Integers.
+class Int
+       fun -: Int is intern
+       fun +(i: Int): Int is intern
+       fun -(i: Int): Int is intern
+       fun *(i: Int): Int is intern
+       fun /(i: Int): Int is intern
+       fun >(i: Int): Bool is intern
+       fun to_f: Float is intern
+end
+
+# Some services about Floats.
+class Float
+       fun +(f: Float): Float is intern
+       fun -(f: Float): Float is intern
+       fun *(f: Float): Float is intern
+       fun /(f: Float): Float is intern
+       fun >(f: Float): Bool is intern
+end
+
+# Booleans, `true` or `false`.
+class Bool end
+
+# Strings (there is no chars...).
+class String end
+
+# List of things.
+class List[E] end
diff --git a/tests/nitsmells.args b/tests/nitsmells.args
new file mode 100644 (file)
index 0000000..0032cd9
--- /dev/null
@@ -0,0 +1,5 @@
+--no-colors test_prog/
+--no-colors TestNitsmells/FeatureEnvy/
+--no-colors TestNitsmells/LargeClass/
+--no-colors TestNitsmells/LongMethod/
+--no-colors TestNitsmells/LongParameterList/
\ No newline at end of file
diff --git a/tests/sav/clusters.res b/tests/sav/clusters.res
new file mode 100644 (file)
index 0000000..1a47c6a
--- /dev/null
@@ -0,0 +1,36 @@
+digraph "G" {
+subgraph "cluster_0" {
+label="process #1";
+style="filled";
+color="lightgrey";
+node[style="filled",color="white",];
+"a0" ;
+"a1" ;
+"a2" ;
+"a3" ;
+"a0" -> "a1" ;
+"a1" -> "a2" ;
+"a2" -> "a3" ;
+};
+subgraph "cluster_1" {
+label="process #2";
+color="blue";
+node[style="filled",];
+"b0" ;
+"b1" ;
+"b2" ;
+"b3" ;
+"b0" -> "b1" ;
+"b1" -> "b2" ;
+"b2" -> "b3" ;
+};
+"start" [shape="Mdiamond",];
+"end" [shape="Msquare",];
+"start" -> "a0" ;
+"start" -> "b0" ;
+"a1" -> "b3" ;
+"b2" -> "a3" ;
+"a3" -> "a0" ;
+"a3" -> "end" ;
+"b3" -> "end" ;
+}
diff --git a/tests/sav/hello.res b/tests/sav/hello.res
new file mode 100644 (file)
index 0000000..8441112
--- /dev/null
@@ -0,0 +1,5 @@
+digraph "G" {
+"hello" ;
+"world" ;
+"hello" -> "world" ;
+}
diff --git a/tests/sav/nitsmells.res b/tests/sav/nitsmells.res
new file mode 100644 (file)
index 0000000..9b27b1c
--- /dev/null
@@ -0,0 +1,3 @@
+Usage: nitsmells [OPTION]... <file.nit>...
+ Computes code smells on Nit programs.
+Use --help for help
diff --git a/tests/sav/nitsmells_args1.res b/tests/sav/nitsmells_args1.res
new file mode 100644 (file)
index 0000000..68b3680
--- /dev/null
@@ -0,0 +1,20 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+test_prog$Character
+Large class: 6 attributes and 18 methods (5.414A 7.161M Average)
+Feature envy:
+       Affected method(s):
+               -total_strengh 4/9
+               -total_endurance 4/9
+               -total_intelligence 4/9
+Long method:  Average 1 lines
+       Affected method(s):
+               -total_strengh has 2 lines
+               -total_endurance has 2 lines
+               -total_intelligence has 2 lines
+-----------
+test_prog::combat$Dwarf
+Feature envy:
+       Affected method(s):
+               -dps 1/3
diff --git a/tests/sav/nitsmells_args2.res b/tests/sav/nitsmells_args2.res
new file mode 100644 (file)
index 0000000..26092c7
--- /dev/null
@@ -0,0 +1,7 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+TestNitsmells$FeatureEnvy
+Feature envy:
+       Affected method(s):
+               -feature_envy_method 2/6
diff --git a/tests/sav/nitsmells_args3.res b/tests/sav/nitsmells_args3.res
new file mode 100644 (file)
index 0000000..fd60962
--- /dev/null
@@ -0,0 +1,5 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+TestNitsmells$LargeClass
+Large class: 18 attributes and 48 methods (17.515A 30.464M Average)
diff --git a/tests/sav/nitsmells_args4.res b/tests/sav/nitsmells_args4.res
new file mode 100644 (file)
index 0000000..1fe9b09
--- /dev/null
@@ -0,0 +1,7 @@
+*** CODE SMELLS METRICS ***
+--- Code Smells Metrics ---
+-----------
+TestNitsmells$LongMethodClass
+Long method:  Average 1 lines
+       Affected method(s):
+               -long_method has 2 lines
diff --git a/tests/sav/undirected_clusters.res b/tests/sav/undirected_clusters.res
new file mode 100644 (file)
index 0000000..a986173
--- /dev/null
@@ -0,0 +1,13 @@
+graph "G" {
+subgraph "clusterA" {
+"a" -- "b" ;
+subgraph "clusterB" {
+"c" -- "d" ;
+};
+};
+subgraph "clusterC" {
+"e" -- "g" ;
+};
+"e" -- "d" ;
+"f" -- "g" ;
+}