Wouter van Ooijen <wouter@voti.nl>: Feb 18 08:33AM +0100 Op 17-Feb-16 om 11:19 PM schreef Ian Collins: > are small and ideally suited to an incremental test/code/refactor cycle > such as TDD. Being close to a pure coding exercise, any objections to > TDD and design are irrelevant at this level. Let's say I write code for a UART to transmit data at a (run-time) specified baudrate. What would your (first) test be? Wouter |
Ian Collins <ian-news@hotmail.com>: Feb 18 08:42PM +1300 Wouter van Ooijen wrote: >> TDD and design are irrelevant at this level. > Let's say I write code for a UART to transmit data at a (run-time) > specified baudrate. What would your (first) test be? It wouldn't be the first test (there would be more generic configuration to test first), but the last test for the requirement would be that the drive sits the correct bits in the the appropriate configuration register(s). -- Ian Collins |
Ian Collins <ian-news@hotmail.com>: Feb 18 08:45PM +1300 Ian Collins wrote: > It wouldn't be the first test (there would be more generic configuration > to test first), but the last test for the requirement would be that the > drive sits the correct bits in the the appropriate configuration It's been a long day... s/drive sits/driver sets/ -- Ian Collins |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 08:58AM +0100 Op 18-Feb-16 om 8:42 AM schreef Ian Collins: >> specified baudrate. What would your (first) test be? > It wouldn't be the first test (there would be more generic configuration > to test first), such as? > but the last test for the requirement would be that the > drive sits the correct bits in the the appropriate configuration > register(s). But how do you determine what is the correct setting (or: how do you make sure that the settings you have determined have the desired effect)? Wouter |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 08:58AM +0100 Op 18-Feb-16 om 8:45 AM schreef Ian Collins: >> to test first), but the last test for the requirement would be that the >> drive sits the correct bits in the the appropriate configuration > It's been a long day... s/drive sits/driver sets/ 9:00 here, a fresh day just started :) Wouter |
Ian Collins <ian-news@hotmail.com>: Feb 18 09:17PM +1300 Wouter van Ooijen wrote: >> It wouldn't be the first test (there would be more generic configuration >> to test first), > such as? That depends on the device, if the UART is stand alone, there might not be any but if it is part of a processor, there will probably configuration registers that have to be setup before the UART can be used. >> register(s). > But how do you determine what is the correct setting (or: how do you > make sure that the settings you have determined have the desired effect)? The device specification! I actually wrote tests for a UART driver on an ARM STM32 processor last year. To verify the settings I had a block of mapped memory at the appropriate address for the device registers and simply tested for the correct values getting written. -- Ian Collins |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 09:22AM +0100 Op 18-Feb-16 om 9:17 AM schreef Ian Collins: >> But how do you determine what is the correct setting (or: how do you >> make sure that the settings you have determined have the desired effect)? > The device specification! Of course, but how do you verify that you derived the correct (and complete) conclusions from the device specification? In my experience *that* is where the errors can be made! (I often make errors - or rather omissions - in that aspect. I don't recall ever making an error afer that: once the correct values are determined writing them is trivial.) Wouter |
Ian Collins <ian-news@hotmail.com>: Feb 18 09:24PM +1300 Ian Collins wrote: > year. To verify the settings I had a block of mapped memory at the > appropriate address for the device registers and simply tested for the > correct values getting written. An example: void TestUART::testConstructDeviceOneAssignsCorrectBaudRateDivider() { const auto baudRate = 100; const auto clockFrequency = 1000; test::STM32F3xxuPClockDriver_GetClock::willReturn(clockFrequency); STM32F3xxUARTDriver( STM32F3xxUARTDriver::USARTPort1, nullptr, 0, baudRate ); TS_ASSERT_EQUALS( clockFrequency/baudRate, usart1->BRR ); } test::STM32F3xxuPClockDriver_GetClock is a mock for another interface, usart1 is an address defined in one of the headers. -- Ian Collins |
Ian Collins <ian-news@hotmail.com>: Feb 18 09:28PM +1300 Wouter van Ooijen wrote: > *that* is where the errors can be made! (I often make errors - or rather > omissions - in that aspect. I don't recall ever making an error afer > that: once the correct values are determined writing them is trivial.) By sanity checking against real hardware. We had some test code from a spike build that validated our understanding of the real hardware. Oh and did I mention pair programming? That's be best way of avoiding those silly errors! -- Ian Collins |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 09:39AM +0100 Op 18-Feb-16 om 9:28 AM schreef Ian Collins: >> omissions - in that aspect. I don't recall ever making an error afer >> that: once the correct values are determined writing them is trivial.) > By sanity checking against real hardware. Now that the test I would start with. Note that I mentioned 'run-time specified baudrate'. Would you test (whether register-values-based or with real hardware) be for one specific baudrate? And would the code you write be for one baudrate only or for the allowed range? Wouter |
Ian Collins <ian-news@hotmail.com>: Feb 18 11:22PM +1300 Wouter van Ooijen wrote: > (whether register-values-based or with real hardware) be for one > specific baudrate? And would the code you write be for one baudrate only > or for the allowed range? Now that is really hard to answer, there are so many ifs buts and maybes! In the example I cited, there was only one baud rate, so we only had to test that the correct number was written to the register. if there were several discrete values, how many to test would depend on how the value was derived. I would probably test the setting of the register and the the algorithm used to calculate the value. The algorithm is just like any other and not specifically driver/embedded code code. Either way my point - that TDD is just as relevant and useful for low level embedded code as it is for business logic - still stands. Code is code. How you implement the details of the tests may vary, but the process does not. -- Ian Collins |
Ian Collins <ian-news@hotmail.com>: Feb 18 11:24PM +1300 Wouter van Ooijen wrote: > Op 18-Feb-16 om 8:45 AM schreef Ian Collins: >> It's been a long day... s/drive sits/driver sets/ > 9:00 here, a fresh day just started :) 23:23 here, time to leave the Linux kernel bugs to fester until morning! -- Ian Collins |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 11:57AM +0100 Op 18-Feb-16 om 11:22 AM schreef Ian Collins: > level embedded code as it is for business logic - still stands. Code is > code. How you implement the details of the tests may vary, but the > process does not. Some messages ago you said that you follow the 3 rules, I am trying to find how you do that in a meaningfull way. Rule 2 says "You are not allowed to write any more of a unit test than is sufficient to fail." And 3 says "You are not allowed to write any more production code than is sufficient to pass the one failing unit test." IMO whenever you add a new baudrate to the set suite, the simplest way to make that test pass is to add an if statement or maybe a switch case or a table entry. Writing a formula that calculates the settings from the baudrate is more complex (reequires more code), so doing that violates rule 3? Wouter |
legalize+jeeves@mail.xmission.com (Richard): Feb 18 05:48PM [Please do not mail me a copy of your followup] Wouter van Ooijen <wouter@voti.nl> spake the secret code >> and get him to champion it. If you can convince Uncle Bob, I'm very >> interested in hearing about it. >I'd rather convert [...] Three words in and we're already into a straw man. You present an opinion that is against TDD and you put words in Uncle Bob's mouth so that you can conveniently make him out to be a zealout and demolish the straw man. When I point out that Uncle Bob is adopting a position based on evidence and experience (not just his, but the experience of many people), you invent another straw man turning it into a religious debate. Positions adopted based on evidence and supporting experience (the team at IBM, the team at MS, etc.) are not religious positions. When I see you respond this way, it makes you out to be the religious zealout, not Uncle Bob. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 07:04PM +0100 Op 18-Feb-16 om 6:48 PM schreef Richard: > You present an opinion that is against TDD and you put words in Uncle > Bob's mouth so that you can conveniently make him out to be a zealout > and demolish the straw man. I did not put any words in his mouth (can you quote any that I did put in?), I just expressed (in a tongue-in-cheeck way) that I don't have much faith in converting him. Maybe more interesting is what I wrote after that fragment: I don't like the three rules when they are interpreted literally. The few talks by Uncle Bob that I have seen seem to indicate that he does. In the follow up I had a reasonable discussion with Ian, which we will probably continue when our timezones permit it. If you want to contribute to the real discussion you are welcome. My main pain point is that to me the rules seem to force me to write only one failing test case, and then just correct the code to pass that case. Other people talk about refactoring, but the three rules don't mention that, so following the 3 rules (and only the 3 rules, as Bob repeatly states in his talks) religiously seems (to me) to rule out refactoring, which seems absurd to me. Wouter van Ooijen |
Ian Collins <ian-news@hotmail.com>: Feb 19 09:29AM +1300 Wouter van Ooijen wrote: > or a table entry. Writing a formula that calculates the settings from > the baudrate is more complex (reequires more code), so doing that > violates rule 3? Not if it still passes the tests. Being able to refactor your code at any stage and know that you haven't broken it is one of the benefits of the process. So yes, you might start off with a chain of ifs and then go back and change the code to use a better algorithm. That's OK, that's good. One of the biggest problems I see with people new to TDD is that they done do what I consider the most important step - refactoring. So they end up with the messy code TDD's detractors complain about. Write a test Pass the test Commit Refactor Commit -- Ian Collins |
Wouter van Ooijen <wouter@voti.nl>: Feb 18 10:00PM +0100 Op 18-Feb-16 om 9:29 PM schreef Ian Collins: >> the baudrate is more complex (reequires more code), so doing that >> violates rule 3? > Not if it still passes the tests. Rule 3 at http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd is "You are not allowed to write any more production code than is sufficient to pass the one failing unit test." In your opinion, does that include refactoring? Wouter |
Ian Collins <ian-news@hotmail.com>: Feb 19 10:10AM +1300 Wouter van Ooijen wrote: > "You are not allowed to write any more production code than is > sufficient to pass the one failing unit test." > In your opinion, does that include refactoring? No. I would amend rule 3 to say "You are not allowed to add functionality to any production code than is sufficient to pass the one failing unit test." Or even "You are not allowed to change the visible behaviour of any production code than is sufficient to pass the one failing unit test." Although that may be a bit too vague. I prefer to teach the write a test, pass the test cycle as adding functionality rather than adding code. Code is too specific in my opinion: you might add functionality by removing code! -- Ian Collins |
Vir Campestris <vir.campestris@invalid.invalid>: Feb 18 09:27PM On 17/02/2016 22:19, Ian Collins wrote: > are small and ideally suited to an incremental test/code/refactor cycle > such as TDD. Being close to a pure coding exercise, any objections to > TDD and design are irrelevant at this level. I'm working on an ALSA driver for an embedded device at the moment. I can't find any decent specs, the hardware doesn't seem to quite behave, and the calls from the kernel are... odd. I have a slight feeling that the whole of Linux was thrown together by a bunch of hackers (good ones) and that the kernel for my device has been changed by other hackers. None of them wrote down what they were intending to do, so when I look at different drivers that do things in different ways I can't really tell which is the new way, and which the old. I can't really write any tests at all - I can't really tell what it's supposed to do, beyond make a noise. And since the I2S DMA is broken that's a long way off. Andy |
Vir Campestris <vir.campestris@invalid.invalid>: Feb 18 09:30PM On 17/02/2016 11:26, Juha Nieminen wrote: > I don't think anybody (who knows anything) is claiming that TDD is > the silver bullet, the perfect tool for every possible situation. Extreme Programming. (We did the training course - then decided not to do it) Andy |
"Öö Tiib" <ootiib@hot.ee>: Feb 17 04:15PM -0800 On Thursday, 18 February 2016 00:58:07 UTC+2, JiiPee wrote: > I have had this many times that I have some global (or static) non-class > function which I want to get access to a certain class but not its > private members. Just to illustrate (not a perfect example..): I have only had such desire with "frozen" code that may not be changed but that clearly lacks some vital functionality. The 'private' isn't security. It just avoids accessing by accident what you don't really want to access. In C++ everything is accessible when you want to: #include <iostream> #include <string> // frozen class class Human { public: Human(std::string name, int id) : m_name(name) , m_ID(id) {} std::string getName() const {return m_name;} private: std::string m_name; int m_ID; /// humans id there are no way to access }; // robbing template (from Johannes Schaub) template<typename Tag, typename Tag::type M> struct Rob { friend typename Tag::type get(Tag) { return M; } }; // preparations to rob Human::m_ID struct Human_id { typedef int Human::*type; friend type get(Human_id); }; template struct Rob<Human_id, &Human::m_ID>; // robbery in action: bool checkHumansLegalStatus(const Human& human) { int id = human.*get(Human_id()); std::cout << "robbed: " << human.getName() << " " << id << '\n'; return id == 42; } int main() { Human jipee {"Jippee", 42}; return checkHumansLegalStatus(jipee); } > What to do? I dont want it to be a friend, to expose ALL privates ... > would be nice if C++ had a "conditional friend" .. so I could say which > members it can get access to , like here. I do not actually understand what you want or don't want. The issue may be that your "class" is getters/setters and its *actual* logic of checking its invariant is elsewhere outside. IOW design error. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 18 02:56AM +0100 On 2/17/2016 11:57 PM, JiiPee wrote: > to other data members which it does not need). Protected kind of > security would be pretty close what would do the job, but there is no > "protected friend". Apparently you want checkHumansLegalStatus to have the same kind of access as a function in a derived class would have to members of an instance of that derived class. There are many ways, and I would probably just use a `static_cast` with a little bit of irrelevant formal UB. But, working entirely within the C++ type system, can go like this: <code> #include <string> namespace zulu { using std::string; class Human { private: string name_; int id_; protected: auto id() const -> int { return id_; } public: auto name() const -> string { return name_; } Human( string const& name, int const id ) : name_( name ), id_( id ) {} }; namespace impl { class Human_x : public Human { public: static auto is_legal( Human const& human ) -> bool { int const id = (human.*&Human_x::id)(); return (id == 42); } }; } // namespace impl auto is_legal( Human const& human ) -> bool { struct Access: Human { static auto is_legal( Human const& human ) -> bool { int const id = (human.*&Access::id)(); return (id == 42); } }; return Access::is_legal( human ); } } // namespace zulu #include <iostream> using namespace std; auto main() -> int { zulu::Human const a( "a", 666 ); zulu::Human const b( "b", 42 ); cout << boolalpha << is_legal( a ) << ' ' << is_legal( b ) << '\n'; } </code> Cheers & hth., - Alf |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Feb 18 02:59AM +0100 On 2/18/2016 2:56 AM, Alf P. Steinbach wrote: Oh sorry, that `namespace impl` shouldn't be there. That's what I wrote first, before I moved that class inside the function. Jeez, why doesn't Thunderbird at least WARN me about these things? Cheers!, - Alf |
JiiPee <no@notvalid.com>: Feb 18 04:29PM but we dont need that class Human_x it runs without that |
JiiPee <no@notvalid.com>: Feb 18 04:28PM On 18/02/2016 01:56, Alf P. Steinbach wrote: > cout << boolalpha << is_legal( a ) << ' ' << is_legal( b ) << '\n'; > } > </code> ok, just compiling and checking... seems to run correctly |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment