Posts Tagged ‘testing’

Maya and PyCharm

Recently at work I had the opportunity of spending a day to get Maya and PyCharm working nicely. It took some tweaking but we’re at a point now where we’re totally happy with our Maya/Python integration.

Remote Debugger: Just follow the instructions here: http://tech-artists.org/forum/showthread.php?3153-Setting-up-PyCharm-for-use-with-Maya-2013. We ended up making a Maya menu item that will add the PyCharm helpers to the syspath and start the pydev debugger client (basically, the only in-Maya instructions in that link).

Autocomplete: To get autocomplete working, we just copied the stubs from “C:\Program Files\Autodesk\Maya2013\devkit\other\pymel\extras\completion\py” and put them into all of the subfolders under: “C:\Users\rgalanakis\.PyCharm20\system\python_stubs” (obviously, change your paths to what you need). We don’t use the mayapy.exe because we do mostly pure-python development (more on that below), and we can’t add the stubs folder to the syspath (see below), so this works well enough, even if it’s a little hacky.

Remote Commands: We didn’t set up the ability to control Maya from PyCharm, we prefer not to develop that way. Setting that up (commandPort stuff, PMIP PyCharm plugin, etc.)  was not worth it for us.

Copy/Paste: To get copy/paste from PyCharm into Maya working, we followed the advice here:  http://forum.jetbrains.com/thread/PyCharm-671 Note, we made a minor change to the code in the last post, because we found screenshots weren’t working at all when Maya was running. We early-out the function if “oldMimeData.hasImage()”.

And that was it. We’re very, very happy with the result, and now we’re running PyCharm only (some of us were using WingIDE or Eclipse or other stuff to remote-debug Maya or work with autocomplete).

————————

A note about the way we work- we try to make as much code as possible work standalone (pure python), which means very little of our codebase is in Maya. Furthermore we do TDD, and we extend that to Maya, by having a base TestCase class that sends commands to Maya under the hood (so the tests run in Maya and report their results back to the pure-python host process). So we can do TDD in PyCharm while writing Maya code. It is confusing to describe but trivial to use (inherit from MayaTestCase, write code that executes in Maya, otherwise treat it exactly like regular python). One day I’ll write up how it works in more depth.

Python Singletons

In my opinion, good python libraries and frameworks should spend effort guiding you towards the ‘pit of success’, rather than trying to keep you from failing. They do this by spending most effort on things related to the critical path- clear interfaces, simple implementations, thorough documentation.

Which is why singletons are, to me, the worst form of framework masturbation in python. You will never be able to stop people from doing something stupid if they’re determined (in pure python). In the case of a singleton, that means instantiating more than one instance of a type. So spending effort on ‘designing’ singletons is not just a waste of effort, but actively harmful. Just provide a clear way to use a single instance, and your system should fail clearly if it detects an actual problem due to multiple instances (as opposed to, trying to detect multiple instances to keep said problem from happening).

The best method for singletons in python, then, is- whatever is simplest!

  1. Some form of module or class state is, to me, the clearest. It requires someone reading or using your code to know nothing more than the most basic python. Just prefix your class def with an underscore, and expose an accessor function to an instance stored on the module (or on the class). The capacity for failure is minimal and the behavior is clear (it requires no behavior modification to the type itself).
  2. Overriding __new__ is pretty bad but OK. It requires someone to understand the subtleties of __new__, which is a useful thing to teach someone but, are singletons really the time and place?
  3. Using a metaclass is a terrible solution. It has a higher likelihood of failure (how many people understand the nuances of metaclasses!?). Misdirection even for people just reading your code, trying to understand your type’s behavior. Avoid.
The question to ask yourself before doing any of this is, “is a singleton a technical requirement or an architectural preference?” Ie, a single instance of an application event loop (QApplication, etc) I’d consider a technical requirement and make it foolproof (in C?). But technical requirements are few and far between and should be driven by underlying system/OS requirements rather than your code’s design or architecture. If it’s an architectural preference- “there should only be one instance of this manager/window/cache”- there’s absolutely no reason to confuse your code (especially you object’s behavior!) to achieve it. Just use design, documentation, and examples, to show people the right way to use it.

Is QA a good stepping stone?

I’ve always heard that it was difficult to move from QA into development (game design/programming/art/production). I thought this was smart- QA people should be there to be QA people, not doing a job only because they hope it would lead to something else.

And at some companies, it works. My previous job was at a well-regarded studio, working on a huge game and IP, in a wonderful city, and lots of developers were looking for work (and we had the money to hire them, if not keep them after ship…). People really wanted to work there (I once interviewed someone as I was near the end of my tenure there, and told him pretty clearly, “you won’t like programming here.” And of course he took the job anyway). It didn’t make sense to spend effort training QA people when you could just hire the people you wanted directly.

However, I now work at a place that is much more difficult to hire for. Outside people respect the company and know the game, but the audience is smaller than one of the biggest IPs and most successful game studios of all time. And especially, it’s in Iceland. Iceland is a wonderful country, but we’ve had several people turn down offers or on-site interviews because their family said “no way” (myself almost included).

In my opinion, it makes more sense to hire QA people (often out of the EVE or local Icelandic community), and set them up on a career path that leads outside of QA. There will never be a serious shortage of applications for a QA job, but it can take a year to hire and relocate a senior person (and you are taking a big risk on that time and expense!). And to a large extent, this is what we do (and while we have sometimes failed at providing career paths, I still think it remains the goal).

I quite like this new strategy- not only does it produce good results over time, it also seems to have more integrity- QA people are too often treated like second class citizens, rather than devs-to-be. I’m not sure if my mind will change if I move on to another studio, but it’s at least how I feel now. I wonder how other studios approach this issue?

(Side note- I am not saying there isn’t a need for senior and permanent QA people, I’m just saying, there are lots of people in QA that don’t want a career in QA. There should also be career development inside of QA, but that shouldn’t be the only career development)

PSA: It isn’t dark magic, it’s your anti-virus software

We’ve all had it- sometimes, under certain conditions, your code errors with an IOError because Windows can’t delete a file. It seems random, and it is. Well, in case you weren’t aware, it’s almost definitely your anti virus software (http://blogs.msdn.com/b/oldnewthing/archive/2012/09/07/10347136.aspx) or at least, some other crazy shit going on.

So a few weeks ago I gave up and said, forget it, I’m just going to write retrying rmtree/remove functions. It took a long time because I refuse to do cargo-cult programming on problems like this, so once I saw that article, I went ahead with it.

I just wanted to spread this information in case you’re in the same situation I was in. You may as well harden and test your little helper retying-delete function I know you have hidden away somewhere, and make it a part of your core libraries.

<No code provided- this is the hacky shit that cannot have a straightforward design and IMO shouldn’t leave your studio, so you’re on your own to figure out an acceptable design and implementation!>

Including test files in coverage reports

We’ve finally gotten our test coverage reports published to an accessible place and I’ve been wondering whether including test files as part of the coverage report is generally preferable to excluding them.

Initially I thought they should be excluded- after all, it’s your ‘real’ code people are generally worried about- but now I’m not so sure. You should probably make sure your test files are actually run and covered, as once you have a large amount of tests that often bend in various ways, it isn’t always a given. And actually that’s just the case  we were in today, we have tests that are still not runnable on our CI server, but the report doesn’t show which ones.

Not to mention having your test source easily browseable in the form of a coverage report, which is easier for test-curious people to browse than a source depot.

I think I’m going to re-include tests in the report, despite it skewing our coverage percentage (favorably :)). But I’m really curious if there’s accepted practice or good reasons either way I haven’t thought of.

TDD for legacy code, graphics code, and legacy graphics code?

We’re currently undergoing a push to do more ‘Agile testing’ at work. At CCP, we “do” Agile pretty well, but I don’t think we “code” Agile really well. A large part (the largest part?) of Agile coding and Agile testing is TDD/Unit Testing, of which I’m a huge fan if not an experienced practitioner.

I know how to do TDD for what I do- that is, side-by-side replacement of an internal legacy codebase in a high-level language. What I don’t have experience in is TDD for expansion and maintenance of a huge, high performance, very active, legacy codebase, and specifically the graphics components and the C++ bits.

So if you have experience in these sorts of things, I’d love to hear them.

At this point I’m sticking my neck out as a TDD and unit testing advocate studio-wide, but am reluctant to evangelize too strongly outside of my areas of expertise. I don’t think it’d be fair to the very talented people in those areas, and I also don’t want to be wrong, even if I know I’m right :) So I’d really like to hear about your experiences with TDD and unit testing in the games and graphics space, and on legacy codebases, because people are coming to me with questions and I don’t have good answers. I’d love to give them places to turn, articles to read, people to contact.

Thanks for any help.

Why I hate Test Driven Development

I have no problem saying that I write good code. I place a focus on TDD and thorough unit and integration testing. I document everything I write (not just function documentation- I document classes, modules, and systems). The fact is, since I’ve been doing these two things somewhat religiously, the amount of time I have spent debugging code has gone down dramatically. There are just not many bugs to find in most of the code I write, they are easy to narrow down when I find them, and they rarely regress.

This is a good thing, isn’t it? So why do I hate TDD?

Because debugging is fun. There, I said it. I love debugging. I think lots of clever people like debugging. I love someone having a problem, coming to me, looking at it together, getting up to walk around, look at the ceiling, talk to myself, stand in front of a whiteboard, draw some lines that spark some idea, try it, manually test a fix out, slouch down in my chair staring at my computer lost in thought, and repeating this until I actually find and fix the problem. Not just think I fixed it, but really sure that I fixed it because suddenly it all makes sense. At which point I spring a terrific boner for my obviously superior brain power that was able to find this problem that plagued mere mortals.

So the sad fact is, since I’ve been doing TDD, I haven’t been able to go on this ego-trip a single time in our TDD-written codebase. Sure, sometimes we get a bug in the UI, but those usually manifest easily. Oh, and I’ve definitely used some frameworks and API’s improperly and caused bugs because of that. But those are the annoying types of debugging we all have to do, not the  magical mystery tour described above.

I didn’t realize how much I missed debugging until it was gone. Fortunately, there’s still lots of legacy code and API’s to get my fix from.

Don’t forget outsourcers!

In the comments on post “Internal Tools Only Require the Critical Path“, Robert Kist points out a few problems to think about when developing internal tools that may have to be used by outsourcers.

I absolutely agree and writing outsource-compliant tools are something many TA’s (including myself) can struggle with. The biggest things are:

Security: Assume your users do not have administrator rights. This shouldn’t be a problem if you are doing things the “right way”, rather than the “hard-coded expedient at the moment” way.

SCM Clients: Do your outsourcers have SCM clients? If not, what does that mean if you scatter your SCM interaction throughout your tools? Are you observing best practices and trying to make your SCM interaction transactional, or are you just making calls at the earliest possible moment?

Network resources: Do your tools require access to network resources, and if so, do the outsourcers have access to them? This could be a database, or a file on the network. Consider how you can break or mock these dependencies (or get rid of them entirely).

Latency: If your tools do require network resources, are they hitting the database/network constantly? If so, expect your tools to be incredibly slow, since instead of access time measured in milliseconds over distances of hundreds of meters, expect access time requiring many seconds over several thousand kilometers. Figure out how to reduce/batch your DB calls/network IO, or mock them out (perhaps provide read-only copies on disk, and point your DB at them).

Machine setup/bootstrapping: What sort of configuration are you relying on, in terms of file setup/folder structure, and in terms of global state of the machine (environment variables, drive letters). Not building in any dependencies on global machine state ensure your tools are much more portable.

Localization/internationalization: Not every international outsourcer speaks English. Many places will translate documentation when settings things up, but that documentation can get stale. We should start thinking about writing localizable code and documentation.

Not so coincidentally- writing good code helps you make more portable tools. A lot of these problems aren’t really problems at all and they don’t really require extra work- they just require care and craftsmanship while developing your tools and code. Focusing on the critical path doesn’t give you a license to write bad code. In fact the contrary. The focus on the critical path means you should have excellent code at all times because the critical path is critical. So if you write good code, as you should, supporting outsourcers will be a lot easier when that time comes.

Do you have any experience or stories writing tools for outsourcers? I’d love to hear about them in the comments.

“Refactor”

Refactoring is defined as a “disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior” by Martin Fowler.

But its everyday usage takes a very different meaning. We use the word refactor to everything from the original meaning, to a complete side-by-side rewrite of a barely-functional system. How can you use the same word to refer to something that does not change external behavior, with building an entirely new replacement system?

More than once, I’ve elicited sighs for my efforts to clarify the language we use. But when you have people that use wildly different vocabularies- artists, programmers, project managers- this is of paramount importance. So the fact that we say ‘refactor’ to meaning any type of rewriting of code or functionality irks me.

So recently I’ve begun a dictionary in my head for the type of tasks we do.

Refactor: The original and ‘precise’ meaning of restructuring a module without altering external behavior, up to a rewrite of parts of a larger system, that may change internal behavior of the system (including the external behavior of large internal components). While I’d love to keep only the more limited original meaning, when we’re dealing with large legacy codebases that often have zero tests, the original meaning doesn’t apply often enough.

Renovate: Rewriting a system so that its external behavior changes considerably but it still fulfills the same purpose (obviously). I name it as such because it is like renovating a building- the exterior and interior may greatly change, but what goes on inside may stay the same. Similar to ‘Rewrite’ but generally applies to a smaller (module/system) scale. An example would be, if you have a library for dealing with source control, and you no longer like the API. So you greatly change how the module works, and it still fulfills its fundamental purpose of dealing with source control.

Recycle: Writing a replacement system side-by-side with the old system, using components from the old system (by either referencing or copy/pasting), with the goal that once the new system is working, the old system will be shut down. The goal is a replacement that is easier to use but fulfills similar requirements. An example would be replacing legacy procedures for data transformations, that may have a lot of imperative code and poor reuse. You would replace the system with something better written, often taking chunks of logic from the old system, or writing tests that verify it produces the same results, then hook up calling code to use the new system, then delete the old one entirely.

Rewrite/Rebuild: When a system is to be written from the ground up, using the original implementation as an example or in a prototype role only, resulting in a system that fulfills the business requirements of the original system but does not necessarily preserve anything else about it. Similar to ‘Renovating’ but generally applies to a larger (application) scale. An example would be, if you have a website/game feature that you want to replace, you’d build the new one to fulfill the same business requirement (we need a website/some feature), but the features may be completely different.

So that’s my personal language right now. My hope is that it will expand to my team and then out from there. I don’t know if I’ll refine it much more- too granular and it would become unwieldy. Do you have suggestions or a language of your own?

Passing around complex objects is the opposite of encapsulation

I see this a lot:

class Foo:
    spam = None
    eggs = None

def frob(foo):
    return sprocket(str(foo.eggs))

f = Foo()
s = frob(f)

It tends to be more sinister, and difficult to see, in verbose examples. But generally it is easily identified by the called method using a single attribute or
method from the object passed in (or multiple in longer functions that should be split up ;) ). Sometimes I bring this up and say, “pass in the value directly,” and the ‘why’ clicks right away. Sometimes people (including my older self) say “but taking in a ‘foo’ encapsulates my method!”

I guess.  It certainly hides the detail that `frob` needs only `.eggs` and doesn’t also need `.spam`. But you’ve also coupled the implementation of `frob` to the interface of `Foo`. So you’ve achieved encapsulation by greatly increasing coupling.

Of the two, I’d vastly prefer a method that must take additional parameters if its implementation changes (ie, if it needs access to `.spam`), than increase coupling. High coupling leads to brittle, untestable, and non-reusable code. Changing the interface of a method leads to… what exactly?

Not only that but the contract of a method is much clearer (to both callers and maintainers) if it takes in meaningful parameters, rather than a single object which it accesses a bunch of properties of. It conveys more information for callers, and establishes what it is supposed to do to maintainers (who will not be able to just get or set the attribute of an object that happened to be passed into that method because it was a convenient place to do so).

So it is usually vastly preferable to take in the values the function uses, rather than pass around complex objects, and in fact this is a common design paradigm in functional programming. But obviously I’m not just using strings and ints everywhere. So what guidelines do I follow?

  1. Immutable objects are fine to pass around (though prefer the advice about just listing what the function takes as per above).
  2. Mutable objects should never be passed around, as I consider creating an object and passing it to a method that mutates it one of the greatest sins in OOP.
Return top
 

Switch to our mobile site